#!/usr/bin/python
#
# Copyright (c) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
# Description:
#
# Checks filenames in specified paths for maximum path length, illegal
# characters, uppercase characters and unknown filename extensions.
#
# http://developer.symbian.org/wiki/index.php/Coding_Standards_and_Conventions#File_and_Directory
#

import os, re, sys, traceback
from optparse import OptionParser

# Patterns for matching illegal characters:
# & ^ + - @ $ % * ( ) | / [ ] { } < > ? ; : , " '
# Space and dash omitted:
re_illegal_chars = re.compile(
    ".*[\&\^\+\@\$\%\*\(\)\|\[\]\{\}\<\>\?\;\:\,\"\'].*")
# Space and dash included:
re_illegal_strictchars = re.compile(
    ".*[\s\&\^\+\-\@\$\%\*\(\)\|\[\]\{\}\<\>\?\;\:\,\"\'].*")

# List of known filename extensions in lowercase and in alphabetical order.
known_exts = [
    ".3gp",
    ".bat", ".bin", ".bmp",
    ".c", ".cat", ".cer", ".cfg", ".class", ".classpath",
     ".classpath_qt", ".classpath_qt_j2me",
    ".cmd", ".comp", ".conf", ".css", ".confml", ".cpp", ".cproject", ".crml",
    ".cvsignore", ".dat", ".dcf", ".def", ".defs", ".dll", ".der", ".doc",
    ".dm", ".dr", ".dso", ".dsp", ".dsw", ".dtd",
    ".emx", ".exe", ".flm", ".gcfml", ".gif",
    ".h", ".hrh", ".htm", ".html", ".iby", ".inf", ".ini", ".inl",
    ".jad", ".jar", ".java", ".javaversion", ".jpeg", ".jpg", ".js", ".jsp", ".jupiter",
    ".key", ".ks", ".launch", ".lib", ".loc", ".log",
    ".mak", ".make", "makefile", "makefile_akn", "makefile_qt",  "manifest", ".mbm", ".mdl",
    ".meta", ".metadata", ".metaxml", ".mf", ".mif", ".mk", ".mmh", ".mmp",
    ".m3g", ".nonqt", ".odc",
    ".pem", ".pkg", ".pl", ".plugin", ".png", ".prefs", ".prf", ".pri", ".pro",
    ".project", ".project_classpath_builder", ".project_normal", ".mtj",
    ".eclipseme", ".properties", ".py",
    "readme", ".reg", ".result", ".review", ".rh", ".rsg", ".rss",
    ".ser", ".sh", ".sis", ".sisx",
    ".sps", ".subsystem", ".svg", ".svgb", ".s60",
    ".template", ".txt", ".vcf", ".vcs",
    ".wav", ".xls", ".xml", ".xpf", ".xsl", ".zip",
    ".rm", ".aac", ".amr", ".awb", ".au", ".jts", ".mid", ".mp3", ".mp4",
    ".ra", ".wma", ".mpg", 
# Peculiar filenames under jrt/tools directory:
    "exemakefile", "ijg_readme", "objmakefile", "staticlibmakefile", "testexemakefile",
    "findtr", "syncqt", "new",
# Other peculiar filenames:
    "installerodclist", "installerodclist30", "midpodclist", "midpodclist30",
    "notice", "package-list", "tckrunnerodclist", "tckrunnerodclist30", "unknowncert","midprootslist",
    "javacontrolpanelodclist", "javacontrolpanelodclist30",
]

# Command line options.
opts = None
# Status code to be returned when script exits.
status_code = 0

def main():
    global opts, status_code
    # Parse command line options and arguments.
    parser = OptionParser(
        usage = "python -u %prog [options] <path> ...",
        description = "Checks filenames in specified paths for maximum " + \
            "path length, illegal characters, uppercase characters and " + \
            "unknown filename extensions. By default makes --length, --chars " + \
            "and --exts checks.")
    parser.add_option("--length", dest="length",
                      action="store_true", default=False,
                      help="check path lengths")
    parser.add_option("--chars", dest="chars",
                      action="store_true", default=False,
                      help="check for illegal characters, ignoring spaces and dashes")
    parser.add_option("--strictchars", dest="strictchars",
                      action="store_true", default=False,
                      help="check for all illegal characters")
    parser.add_option("--uppercase", dest="uppercase",
                      action="store_true", default=False,
                      help="check for uppercase characters")
    parser.add_option("--exts", dest="exts",
                      action="store_true", default=False,
                      help="check for unknown filename extensions")
    (opts, args) = parser.parse_args()
    if not (opts.length or opts.chars or opts.strictchars or \
            opts.uppercase or opts.exts):
        # No checks specified, make all checks except uppercase and strictchars.
        opts.length = True
        opts.chars = True
        opts.strictchars = False
        opts.uppercase = False
        opts.exts = True
    paths = []
    for arg in args:
        paths += [arg]

    try:
        # Check all filenames under paths that were given as arguments.
        prefix_path = "jrt"
        for path in paths:
            check_filenames(prefix_path, path)
    except:
        print "ERROR: Unexpected exception"
        traceback.print_exc()
        sys.exit(1)

    # If no exceptions occurred, exit with status_code.
    sys.exit(status_code)

def check_filenames(prefix_path, path):
    for root, dirs, files in os.walk(path):
        if root.find(path + os.sep) == 0:
            repl_prefix = path + os.sep
        else:
            repl_prefix = path
        dir_path = os.path.join(prefix_path, root.replace(repl_prefix, ""))
        for dir in dirs:
            dp_path = os.path.join(dir_path, dir, "distribution.policy.s60")
            if not os.path.exists(dp_path):
                check_filename(dp_path)
            check_dirname(os.path.join(root, dir))
        for file in files:
            file_path = os.path.join(dir_path, file)
            check_filename(file_path)

def check_dirname(dirname):
    # Ignore .svn paths.
    if dirname.find(os.sep + ".svn" + os.sep) != -1:
        return
    global status_code
    if not os.listdir(dirname):
        print "Empty directory " + dirname
        status_code = 1

def check_filename(filename):
    # Ignore .svn paths.
    if filename.find(os.sep + ".svn" + os.sep) != -1:
        return
    global opts, re_illegal_chars, re_illegal_strictchars, known_exts, status_code
    if opts.length:
        # Check maximum path length.
        l = len(filename)
        if l > 140:
            print "Too long path (" + str(l) + "): " + filename
            status_code = 1
    if opts.strictchars:
        # Check for all illegal characters.
        if re_illegal_strictchars.match(filename):
            print "Illegal character: " + filename
            status_code = 1
    elif opts.chars:
        # Check for illegal characters.
        if re_illegal_chars.match(filename):
            print "Illegal character: " + filename
            status_code = 1
    path = os.path.dirname(filename)
    (root, ext) = os.path.splitext(filename)
    if opts.uppercase:
        if ext == ".java" or ext == ".class":
            # Check for uppercase characters in dir name.
            if path.lower() != path:
                print "Uppercase character: " + filename
                status_code = 1
        else:
            # Check for uppercase characters.
            if filename.lower() != filename:
                print "Uppercase character: " + filename
                status_code = 1
    if opts.exts:
        # Check for unknown extensions.
        if ext == "":
            ext = os.path.basename(filename)
        if not ext.lower() in known_exts:
            print "Unknown extension: " + filename
            status_code = 1

if __name__ == "__main__":
    main()
