#! /usr/bin/python
#
# This script takes the following input:
# 1) A set of directories.
# 2) A set of file masks that are used to select files from those
#    directories.
# 3) A dictionary file listing words that need replacing in those files.
# 4) Options to specify what type of action is to be taken:
#    * Output a 'diff' style output of what would be changed.
#    * Output a concise output of what would be changed.
#    * Go ahead and make the changes.
# 
# See the usage() function below for latest usage instructions.
#

import os
import sys

# need to add dir containing this script to the sys.path
mydir = os.path.dirname(sys.argv[0])
if len(mydir) == 0: libdir = 'lib'
else: libdir = os.path.join(mydir, 'lib')
sys.path.insert(0, libdir)

import fnmatch
import getopt
import re
import string

from dirwalker import DirWalker
from diffwalker import DiffWalker
import namedict

defaultSourceFileMasks = ['*.h', '*.cpp', '*.c', '*.i',
                          '*.cc', '*.cp', '*.in',
                          '*.rc', '*.rc2', '*.H', '*.CPP']

## print usage information
def usage():
    print 'namereplace.py [--help] \\'
    print '               [--diff | --concise] [--context=N] [--do-edits] \\'
    print '               [--orig-suffix=.SUFFIX] --dict=DICT_FILE \\'
    print '               [--verbose] \\'
    print '               [--word-chars=REGEXP] \\'
    print '               [--filenames] \\'
    print '               [--source-files] \\'
    print '               [FILE_MASKs...] [DIRs...] [FILEs...]'
    print 'NOTEs:'
    print '  * FILE_MASKs, FILEs and DIRs can be intermixed.  It is considered'
    print '    a FILE_MASK if it contains a "*" or "?" character.'
    print '  * The --context option specifies the number of unchanged lines of'
    print '    context to include around blocks of diff output. The default is 3.'
    print '  * The --orig-suffix option specifies a suffix that is appended to'
    print '    a filename to preserve the original contents of the file when the'
    print '    --do-edits option is given. The default is to just overwrite the'
    print '    original without saving it.'
    print '  * The --word-chars option specifies a portion of a regular expression. That'
    print '    regexp is used to match a single character that is part of a "word"'
    print '    of input.  It defaults to letters, numbers and underscore.'
    print '  * The --filenames option changes the default for --word-chars to be'
    print "    suitable for filenames. It sets the default to be '[\\w.]'"
    print '  * The --source-files option adds a set of standard source file masks'
    print '    to the FILE_MASKs:'
    print '      %s' % string.join(defaultSourceFileMasks)
    print
    print 'Defaults:'
    print "    namereplace.py --diff --context=3 --word-chars='\\w' ."
    print "    (don't forget to escape the * and ? chars to get them past your shell,"
    print "     and similarly for special characters in the --word-chars regexp)"
    print
    sys.exit(1)

def runcmd(argv):
    opt_list, arg_list = getopt.getopt(argv[1:], '',
                                       ['help', 'diff', 'concise', 'context=',
                                        'dict=', 'verbose', 'word-chars=',
                                        'filenames', 'source-files',
                                        'do-edits', 'orig-suffix='])

    if len(arg_list) == 0 and len(opt_list) == 0: usage()

    ## create a hash from the opt_list
    opt_hash = {}
    for opt in opt_list:
        optName = re.sub(r'--', '', opt[0])
        opt_hash[optName] = opt[1]

    if opt_hash.has_key('verbose'):
        print "# Options: %s" % opt_hash

    if opt_hash.has_key('help'): usage()

    if not opt_hash.has_key('dict'):
        print "Need to specify a dictionary file for replacements"
        sys.exit(1)

    # default to --diff if neither output type specified
    if opt_hash.has_key('diff'):
        if opt_hash.has_key('concise'):
            print "Need to specify only one of --diff & --concise"
            sys.exit(1)
    elif not opt_hash.has_key('concise'):
        opt_hash['diff'] = 1

    if not opt_hash.has_key('word-chars'):
        if opt_hash.has_key('filenames'): opt_hash['word-chars'] = r'[\w.]'
        else: opt_hash['word-chars'] = r'\w'

    file_list = []
    mask_hash = {}
    for arg in arg_list:
        if re.match(r'[*\?]', arg): mask_hash[arg] = 1
        else: file_list.append(arg)

    if len(file_list) == 0: file_list.append('.')
    if len(mask_hash) == 0 or opt_hash.has_key('source-files'):
        for mask in defaultSourceFileMasks:
            mask_hash[mask] = 1

    nd = namedict.ReadDict(opt_hash['dict'], opt_hash['word-chars'])
    if len(nd) <= 0:
        print "No replacements found in dictionary %s" % opt_hash['dict']
        sys.exit(1)

    if opt_hash.has_key('verbose'): print "# dict=%s" % nd

    dw = DiffWalker(mask_hash.keys(), opt_hash, nd)
    for f in file_list:
        dw.Walk(f)

## MAIN
if __name__ == '__main__':
    if os.environ.has_key('NAMEREPLACE_ARGS'):
        runcmd(['namereplace.py'] + string.split(os.environ['NAMEREPLACE_ARGS']))
    else: runcmd(sys.argv)
