From a5bb115313d0e3fc896b0b5a6746f3900ab999fb Mon Sep 17 00:00:00 2001 From: Chris Koeritz Date: Thu, 2 Feb 2017 12:49:06 -0500 Subject: [PATCH] added missing did not get the full set of new things again. argh. need better scripts for working on git branches. --- scripts/buildor/buildor_make_code_writable.sh | 20 ++ scripts/buildor/upgrade_hoople_to_yeti.sh | 121 +++++++++ scripts/buildor/vistu_find_mangled_names.sh | 6 + scripts/buildor/vistu_locate_function.sh | 15 + scripts/buildor/vistu_zap_msbuilds.sh | 6 + scripts/database/create_marks.sh | 50 ++++ scripts/database/info_overload_report.sh | 147 ++++++++++ scripts/system/apache_log_sorter.sh | 13 + scripts/system/synch_to_mtp_device.sh | 32 +++ scripts/text/phrase_replacer.py | 257 ++++++++++++++++++ 10 files changed, 667 insertions(+) create mode 100644 scripts/buildor/buildor_make_code_writable.sh create mode 100644 scripts/buildor/upgrade_hoople_to_yeti.sh create mode 100644 scripts/buildor/vistu_find_mangled_names.sh create mode 100644 scripts/buildor/vistu_locate_function.sh create mode 100644 scripts/buildor/vistu_zap_msbuilds.sh create mode 100644 scripts/database/create_marks.sh create mode 100644 scripts/database/info_overload_report.sh create mode 100644 scripts/system/apache_log_sorter.sh create mode 100644 scripts/system/synch_to_mtp_device.sh create mode 100644 scripts/text/phrase_replacer.py diff --git a/scripts/buildor/buildor_make_code_writable.sh b/scripts/buildor/buildor_make_code_writable.sh new file mode 100644 index 00000000..494159d0 --- /dev/null +++ b/scripts/buildor/buildor_make_code_writable.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +source $FEISTY_MEOW_SCRIPTS/buildor/seek_all_source.sh + +function strip_file { + file=$1 + perl $FEISTY_MEOW_SCRIPTS/strip_cr.pl $file +} + +#echo tempfile is $SOURCES_FOUND_LIST + +# this block has io redirected from the file to std in. +while true; do + read line_found + if [ $? != 0 ]; then break; fi + chmod u+w "$line_found" +done <$SOURCES_FOUND_LIST + +rm $SOURCES_FOUND_LIST # clean up. + diff --git a/scripts/buildor/upgrade_hoople_to_yeti.sh b/scripts/buildor/upgrade_hoople_to_yeti.sh new file mode 100644 index 00000000..0661062a --- /dev/null +++ b/scripts/buildor/upgrade_hoople_to_yeti.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +file="$1"; shift +if [ ! -f "$file" ]; then + echo must pass filename on command line. + exit 3 +fi + +tempfile="$(mktemp "$TMP/zz_temp_codefix.XXXXXX")" + +#echo temp file is $tempfile + +cat "$file" \ + | sed -e 's/command_line::__arg/application::__arg/g' \ + | sed -e 's/IMPLEMENT_CLASS_NAME/DEFINE_CLASS_NAME/g' \ + | sed -e 's/istring/astring/g' \ + | sed -e 's/byte_format\([^t]\)/byte_formatter\1/g' \ + | sed -e 's/isprintf/a_sprintf/g' \ + | sed -e 's/portable::sleep_ms/time_control::sleep_ms/g' \ + | sed -e 's/portable::env_string/environment::get/g' \ + | sed -e 's/portable::launch_process/launch_process::run/g' \ + | sed -e 's/portable::application_name/application_configuration::application_name/g' \ + | sed -e 's/portable::process_id/application_configuration::process_id/g' \ + | sed -e 's/log_base::platform_ending/parser_bits::platform_eol_to_chars/g' \ + | sed -e 's/ithread/ethread/g' \ + | sed -e 's/timed_object/timeable/g' \ + | sed -e 's/utility::timestamp(/time_stamp::notarize(/g' \ + | sed -e 's/anchor_window/hoople_service/g' \ + | sed -e 's/basis::attach/structures::attach/g' \ + | sed -e 's/basis::detach/structures::detach/g' \ + | sed -e 's/portable::system_error/critical_events::system_error/g' \ + | sed -e 's/basis::pack\([^a]\)/structures::pack_array\1/g' \ + | sed -e 's/basis::unpack/structures::unpack_array/g' \ + | sed -e 's/ *$//g' \ + | sed -e 's/^#include *$//g' \ + | sed -e 's/^#include *$//g' \ + | sed -e 's/^#include *$//g' \ + | sed -e 's/^#include *$//g' \ + | sed -e 's/class infoton_list;//g' \ + | sed -e 's/^#include "[_a-zA-Z0-9]*_dll.h" *$//g' \ + | sed -e 's/^#include "dll_[_a-zA-Z0-9]*.h" *$//g' \ + | sed -e 's/^#ifndef .*IMPLEMENTATION_FILE *$//g' \ + | sed -e 's/^#define .*IMPLEMENTATION_FILE *$//g' \ + | sed -e 's/^#endif .*IMPLEMENTATION_FILE *$//g' \ + | sed -e 's/convert_utf/utf_conversion/g' \ + | sed -e 's/mechanisms\/time_stamp/timely\/time_stamp/g' \ + | sed -e 's/mechanisms\/roller/structures\/roller/g' \ + | sed -e 's/mechanisms\/safe_roller/processes\/safe_roller/g' \ + | sed -e 's/basis.string_array/structures\/string_array/g' \ + | sed -e 's/opsystem.application_shell/application\/application_shell/g' \ + | sed -e 's/opsystem.filename/filesystem\/filename/g' \ + | sed -e 's/opsystem.heavy_file_ops/filesystem\/heavy_file_ops/g' \ + | sed -e 's/opsystem.huge_file/filesystem\/huge_file/g' \ + | sed -e 's/opsystem.application_base/application\/base_application/g' \ + | sed -e 's/opsystem.command_line/application\/command_line/g' \ + | sed -e 's/opsystem.directory/filesystem\/directory/g' \ + | sed -e 's/opsystem.rendezvous/application\/rendezvous/g' \ + | sed -e 's/opsystem.singleton_application/application\/singleton_application/g' \ + | sed -e 's/opsystem.timer_driver/timely\/timer_driver/g' \ + | sed -e 's/opsystem.ini_config/configuration\/ini_configurator/g' \ + | sed -e 's/opsystem.path_config/configuration\/application_config/g' \ + | sed -e 's/opsystem.byte_filer/filesystem\/byte_filer/g' \ + | sed -e 's/sockets.address/sockets\/internet_address/g' \ + | sed -e 's/path_configuration/application_configuration/g' \ + | sed -e 's/mechanisms.timer/timely\/stopwatch/g' \ + | sed -e 's/mechanisms.ethread/processes\/ethread/g' \ + | sed -e 's/mechanisms.safe_callback/processes\/safe_callback/g' \ + | sed -e 's/mechanisms.thread_cabinet/processes\/thread_cabinet/g' \ + | sed -e 's/basis.chaos/mathematics\/chaos/g' \ + | sed -e 's/[A-Z_][A-Z_]*CLASS_STYLE //g' \ + | sed -e 's/[A-Z_][A-Z_]*FUNCTION_STYLE //g' \ + | sed -e 's/\([^:]\)u_int/\1basis::u_int/g' \ + | sed -e 's/\([^:]\)u_short/\1basis::u_short/g' \ + | sed -e 's/class astring;/#include /g' \ + | sed -e 's/class int_set;/#include /g' \ + | sed -e 's/class int_roller;/#include /g' \ + | sed -e 's/class outcome;/#include /g' \ + | sed -e 's/class mutex;/#include /g' \ + | sed -e 's/class ethread;/#include /g' \ + | sed -e 's/class byte_filer;/#include /g' \ + | sed -e 's/class string_array;/#include /g' \ + | sed -e 's/class string_table;/#include /g' \ + | sed -e 's/class byte_array;/#include /g' \ + | sed -e 's/class string_set;/#include /g' \ + | sed -e 's/class time_stamp;/#include /g' \ + | sed -e 's/class directory_tree;/#include /g' \ + | sed -e 's/class filename_list;/#include /g' \ + | sed -e 's/class chaos;/#include /g' \ + | sed -e 's/class configurator;/#include /g' \ + | sed -e 's/class unique_int;/#include /g' \ + | sed -e 's/class tcpip_stack;/#include /g' \ + | sed -e 's/class safe_roller;/#include /g' \ + | sed -e 's/class blowfish_crypto;/#include /g' \ + | sed -e 's/class RSA_crypto;/#include /g' \ + | sed -e 's/class entity_data_bin;/#include /g' \ + | sed -e 's/class infoton;/#include /g' \ + | sed -e 's/class octopus_request_id;/#include /g' \ + | sed -e 's/class internet_address;/#include /g' \ + | sed -e 's/class machine_uid;/#include /g' \ + | sed -e 's/class spocket;/#include /g' \ + | sed -e 's/class encryption_tentacle;/#include /g' \ + | sed -e 's/class login_tentacle;/#include /g' \ + | sed -e 's/class thread_cabinet;/#include /g' \ + | sed -e 's/RSA_crypto/rsa_crypto/g' \ + | sed -e 's/float_plus/double_plus/g' \ + | sed -e 's/basis::obscure_/structures::obscure_/g' \ + | sed -e 's/program_wide_logger()/program_wide_logger::get()/g' \ + | sed -e 's/textual.tokenizer/configuration\/tokenizer/g' \ + | sed -e 's/\([^_]\)tokenizer/\1variable_tokenizer/g' \ + | sed -e 's/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/[\/]*/\/\/\/\/\/\/\/\/\/\/\/\/\/\//g' \ + >"$tempfile" + +mv "$tempfile" "$file" + + diff --git a/scripts/buildor/vistu_find_mangled_names.sh b/scripts/buildor/vistu_find_mangled_names.sh new file mode 100644 index 00000000..de35a4fe --- /dev/null +++ b/scripts/buildor/vistu_find_mangled_names.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# this script takes one microsoft compiler output file and tries to find the +# mangled C++ function names contained in it. +file=$1 +dumpbin //all "$file" | grep SECREL | sed -e 's/^.*0000C *[0-9a-fA-F][0-9a-fA-F] *\([^ ]*\).*$/\1/' + diff --git a/scripts/buildor/vistu_locate_function.sh b/scripts/buildor/vistu_locate_function.sh new file mode 100644 index 00000000..6a49c1e5 --- /dev/null +++ b/scripts/buildor/vistu_locate_function.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +filename="$1"; shift +function_name="$1"; shift + +good_path="$(cygpath -w -s $filename)" + +#/exports +dumpbin /all $good_path | grep -q -i "$function_name" +if [ $? -eq 0 ]; then + echo "Found $function_name in $filename" +fi + + + diff --git a/scripts/buildor/vistu_zap_msbuilds.sh b/scripts/buildor/vistu_zap_msbuilds.sh new file mode 100644 index 00000000..57c8e744 --- /dev/null +++ b/scripts/buildor/vistu_zap_msbuilds.sh @@ -0,0 +1,6 @@ +#!/bin/bash +# this simple script kills off some troublesome processes in preparation for a new build +# with visual studio. +zap_process.exe msbuild.exe +zap_process.exe mspdbsrv.exe + diff --git a/scripts/database/create_marks.sh b/scripts/database/create_marks.sh new file mode 100644 index 00000000..ab9f2669 --- /dev/null +++ b/scripts/database/create_marks.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# this script rebuilds the bookmarks files. it requires the variables: +# WEBBED_SITES: points at the root of the web hierarchy. + +export GRUNTOSE_DIR=$WEBBED_SITES/gruntose.com + +rootname=$HOME/generated +suffix=.html +norm_add=_marks +js_add=_js_tree_marks +moz_add=_moz_bookmarks + +newmarx=${rootname}_links.csv +genlinx=$rootname$norm_add$suffix +genlinx_js=$rootname$js_add$suffix +genlinx_moz=$rootname$moz_add$suffix + +if [ -f $genlinx ]; then rm $genlinx; fi +if [ -f $newmarx ]; then rm $newmarx; fi +if [ -f $genlinx_js ]; then rm $genlinx_js; fi +if [ -f $genlinx_moz ]; then rm $genlinx_moz; fi + +$RUNTIME_PATH/binaries/marks_sorter -i $GRUNTOSE_DIR/Info/Twain/links_db.csv -o $newmarx +if [ $? != 0 ]; then + echo error during sorting of the bookmarks. + exit 1 +fi + +$RUNTIME_PATH/binaries/marks_maker -i $GRUNTOSE_DIR/Info/Twain/links_db.csv -t $GRUNTOSE_DIR/Info/Twain/marks_template.html -o $genlinx -s human +if [ $? != 0 ]; then + echo error during creation of the normal web page of bookmarks. + exit 1 +fi + +$RUNTIME_PATH/binaries/marks_maker -i $GRUNTOSE_DIR/Info/Twain/links_db.csv -t $GRUNTOSE_DIR/Info/Twain/marks_template.html -o $genlinx_moz -s mozilla +if [ $? != 0 ]; then + echo error during creation of the mozilla format page of bookmarks. + exit 1 +fi + +$RUNTIME_PATH/binaries/js_marks_maker -i $GRUNTOSE_DIR/Info/Twain/links_db.csv -t $GRUNTOSE_DIR/Info/Twain/js_template.html -o $genlinx_js +if [ $? != 0 ]; then + echo error during creation of the javascript bookmark page. + exit 1 +fi + +\mv -f $genlinx $genlinx_moz $genlinx_js $GRUNTOSE_DIR/Info/Twain +\mv -f $newmarx $TMP + diff --git a/scripts/database/info_overload_report.sh b/scripts/database/info_overload_report.sh new file mode 100644 index 00000000..38091138 --- /dev/null +++ b/scripts/database/info_overload_report.sh @@ -0,0 +1,147 @@ + +# these metrics are how bogged down we are in to-do type items. + +REPORT_FILE="$HOME/cloud/fred_stats/overload_history.txt" + +# given a path, this will find how many items are under it, ignoring svn and git files, plus +# other patterns we happen to notice are not useful. +function calculate_count() +{ + local dir="$1"; shift + local count=$(find "$dir" -type f -exec echo \"{}\" ';' 2>/dev/null | grep -v "\.svn" | grep -v "\.git"| grep -v "\.basket" | grep -v "\.version" | grep -v "\.keep" | wc -l | tr -d ' ') + if [ -z "$count" ]; then echo 0; else echo "$count"; fi +} + +# calculates the size in kilobytes of all the note files in a hierarchy. +# this is just a raw statistic for how much content all those notes make up. since +# we have not separated out all the to-dos in some files (most notably the metaverse +# backlogs and to-do lists), it's good to also know what kind of girth the notes have. +function calculate_weight() +{ + local dir="$1"; shift + local weight=$(find "$dir" -type f -exec echo \"{}\" ';' 2>/dev/null | grep -v "\.svn" | grep -v "\.git"| grep -v "\.basket" | grep -v "\.version" | grep -v "\.keep" | xargs ls -al | awk '{ print $5 }' | paste -sd+ | bc 2>/dev/null) + if [ -z "$weight" ]; then echo 0; else echo "$weight"; fi +} + +# calculate_complexity gets a very simple metric of how many directory components are +# present at the target location and below. +function calculate_complexity() +{ + local dir="$1"; shift + local complexity=$(find "$dir" -type d | wc -l) + if [ -z "$complexity" ]; then echo 0; else echo "$complexity"; fi +} + +# produces a report line in our format. +function format_report_line() +{ + local count="$1"; shift + local weight="$1"; shift + weight=$((weight / 1024)) + local complexity="$1"; shift + echo "$count\t${complexity}\t\t${weight}\t\t$*\n" +} + +# two parameters are needed: the directory to sum up and the label to use for it in the report. +# this will calculate the count and weight for a hierarchy of notes, and then produce a +# line of reporting for those. +function analyze_hierarchy_and_report() +{ + local dir="$1"; shift + local label="$1"; shift + local count=$(calculate_count "$dir") + total_overload=$(($count + $total_overload)) + local weight=$(calculate_weight "$dir") + total_weight=$(($total_weight + $weight)) + local complexity=$(calculate_complexity "$dir") + total_complexity=$(($total_complexity + $complexity)) + full_report+=$(format_report_line "$count" "$weight" "$complexity" "$label") +} + +# scans through items in the notes folder that begin with a pattern. +# each of those is treated as an aggregable portion of the report. +# first parameter is the title in the report, second and so on are +# a list of directory patterns to scan and aggregate. +function analyze_by_dir_patterns() +{ + local title="$1"; shift + local hier_count=0 + local hier_weight=0 + local hier_complexity=0 + for folder in $@; do + temp_count=$(calculate_count $folder) + hier_count=$(($hier_count + $temp_count)) + temp_weight=$(calculate_weight $folder) + hier_weight=$(($hier_weight + $temp_weight)) + temp_complexity=$(calculate_complexity $folder) + hier_complexity=$(($hier_complexity + $temp_complexity)) + done + total_overload=$(($hier_count + $total_overload)) + total_weight=$(($total_weight + $hier_weight)) + total_complexity=$(($total_complexity + $hier_complexity)) + full_report+=$(format_report_line "$hier_count" "$hier_weight" "$hier_complexity" "$title") +} + +############## + +# reset these before we add anything... +total_overload=0 +total_weight=0 + +# start out the report with a header. +full_report="\ +\n\ +current information overload consists of:\n\ +\n\ +" +full_report+="count\tcomplexity\tweight (kb)\tcategory\n\ +================================================================\n\ +" + +analyze_hierarchy_and_report ~/cloud/urgent "high priority" + +# notes are individual files of tasks, usually, although some are combined. +analyze_hierarchy_and_report ~/cloud/grunty_notes "grunty notes (external facing things?)" + +# feisty notes are about feisty meow(r) concerns ltd codebase development. +analyze_hierarchy_and_report ~/cloud/feisty_notes "feisty meow notes (code related)" + +# home notes are a new top-level category; used to be under the grunty. +analyze_hierarchy_and_report ~/cloud/branch_road "hearth and home notes (branch road, yo!)" + +# games and fun stuff. not sure why these count as backlog, but whatever. +analyze_hierarchy_and_report ~/cloud/games_yo "games, yo!" + +# some source code that needs to be sucked into other places, other codebases. they are not +# supposed to pile up here. but they have, so we track them. +analyze_hierarchy_and_report ~/cloud/scavenging_source "source scavenging" + +# and then count up the things that we think will be cleaned soon, but one thing we have learned +# unsorted files haven't been categorized yet. +analyze_hierarchy_and_report ~/cloud/disordered "unsorted files" + +# we now consider the backlog of things to read to be a relevant fact. this is going to hose +# up our weight accounting considerably. +analyze_hierarchy_and_report ~/cloud/reading "reading list" + +# scan all the items declared as active projects. +analyze_by_dir_patterns "active items" ~/cloud/active* + +# scan across all appropriately named project or research folders that live in the "cloud". +analyze_by_dir_patterns "project files" ~/cloud/project* ~/cloud/research* + +# snag any work related items for that category. +analyze_by_dir_patterns "job and work tasks" ~/cloud/job* + +# scan all the trivial project folders. +analyze_by_dir_patterns "trivial items" ~/cloud/trivia* + +full_report+="================================================================\n\ +" +full_report+="$(format_report_line "$total_overload" "$total_weight" "$total_complexity" "total overload")" +full_report+="\n\ +[gathered on $(date)]\n\n\ +##############" + +echo -e "$full_report" | tee -a "$REPORT_FILE" + diff --git a/scripts/system/apache_log_sorter.sh b/scripts/system/apache_log_sorter.sh new file mode 100644 index 00000000..5da498c3 --- /dev/null +++ b/scripts/system/apache_log_sorter.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if [ ! -f $1 ]; then + echo "Usage: $0 " + echo "This script needs a file to sort and a new name for the file after sorting." + exit 1 +fi + +echo "Sorting $1 into $2" + +sort -t ' ' -k 4.9,4.12n -k 4.5,4.7M -k 4.2,4.3n -k 4.14,4.15n -k 4.17,4.18n -k 4.20,4.21n $1 > $2 + + diff --git a/scripts/system/synch_to_mtp_device.sh b/scripts/system/synch_to_mtp_device.sh new file mode 100644 index 00000000..254ed7f1 --- /dev/null +++ b/scripts/system/synch_to_mtp_device.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +sourcedir="$1"; shift +targetdir="$1"; shift + +# where we will look for mtp devices. +mtp_base_path="/run/user/$UID/gvfs/mtp\:host\=" + +if [ -z "$sourcedir" -o -z "$targetdir" ]; then + echo "This script needs source and target directory names that can be synched" + echo "between the computer's file system and a USB drive mounted with the mtp" + echo "protocol. The folder on the USB drive should include the entire path except" + echo "for the device mount location. For example:" + echo " $(basename $0) ebooks \"/Internal\ Storage/My\ Files/ebooks\"" + exit 1 +fi + +# the mtp part will flux. if there is more than one device mounted, this will hose up. +#checking for more than one device there: +mtpdevices=("$mtp_base_path"*) +if [ ${#mtpdevices[@]} -ne 1 ]; then + echo "There is more than one MTP device mounted. This script requires exactly one" + echo "MTP device mounted at a time. Sorry." + exit 1 +elif [ ! -d "${#mtpdevices[@]}" ]; then + echo "The MTP device does not seem to be mounted currently. The path did not" + echo "expand properly." + exit 1 +fi + +rsync -rv --exclude *.git --exclude *.svn "$sourcedir" "${mtpdevices[0]}/$targetdir" + diff --git a/scripts/text/phrase_replacer.py b/scripts/text/phrase_replacer.py new file mode 100644 index 00000000..a7ea0155 --- /dev/null +++ b/scripts/text/phrase_replacer.py @@ -0,0 +1,257 @@ +#!/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. + + -- 2.34.1