diff --git a/CMakeLists.txt b/CMakeLists.txt index e3b1225..9c1b9b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,30 +1,74 @@ cmake_minimum_required(VERSION 2.6) +# For custom cmake modules. +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +include(CompileCheck) +include(VersionString) # TODO use this + +# Force re-checking libraries if the compiler or compiler flags change. +if((NOT LAST_CMAKE_CXX_FLAGS STREQUAL CMAKE_CXX_FLAGS) + OR (NOT LAST_CMAKE_CXX_COMPILER STREQUAL CMAKE_CXX_COMPILER)) + force_recheck_library(Boost) + unset(Boost_INCLUDE_DIR CACHE) + set(LAST_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" CACHE INTERNAL "The last C++ compiler flags.") + set(LAST_CMAKE_CXX_COMPILER "${CMAKE_CXX_COMPILER}" CACHE INTERNAL "The last C++ compiler.") +endif() + unset(LIBRARIES) find_package(Boost REQUIRED COMPONENTS iostreams filesystem date_time) +check_link_library(Boost Boost_LIBRARIES) list(APPEND LIBRARIES "${Boost_LIBRARIES}") link_directories("${Boost_LIBRARY_DIRS}") include_directories(SYSTEM "${Boost_INCLUDE_DIR}") -#find_package(ZLIB) -#if(ZLIB_FOUND) -# include_directories(SYSTEM "${ZLIB_INCLUDE_DIR}") -# list(APPEND LIBRARIES "${ZLIB_LIBRARIES}") -# add_definitions(-DHAVE_ZLIB) -#endif() +find_package(LZMA) +check_link_library(LZMA LZMA_LIBRARIES) +list(APPEND LIBRARIES "${LZMA_LIBRARIES}") +include_directories(SYSTEM "${LZMA_INCLUDE_DIR}") + +# TODO make LZMA optional + +# TODO debugging-only +add_cxxflag("-ggdb") +add_cxxflag("-O3") + +# TODO should probably be supplied by the user +add_cxxflag("-march=native") +add_cxxflag("-Wl,--as-needed") -#find_package(BZip2) -#if(BZIP2_FOUND) -# include_directories(SYSTEM "${BZIP2_INCLUDE_DIR}") -# list(APPEND LIBRARIES "${BZIP2_LIBRARIES}") -# add_definitions(-DHAVE_BZIP2) -#endif() +add_cxxflag("-Wall") +add_cxxflag("-Wextra") +add_cxxflag("-Wformat=2") +add_cxxflag("-Wundef") +add_cxxflag("-Wpointer-arith") +add_cxxflag("-Wcast-qual") +add_cxxflag("-Woverloaded-virtual") +add_cxxflag("-Wlogical-op") +add_cxxflag("-Wliteral-conversion") +add_cxxflag("-Wshift-overflow") +add_cxxflag("-Woverflow") +add_cxxflag("-Wbool-conversions") +add_cxxflag("-Wconversion") +add_cxxflag("-Wsign-conversion") +add_cxxflag("-Wmissing-declarations") +add_cxxflag("-Wredundant-decls") -list(APPEND LIBRARIES -llzma) +if(DEBUG_EXTRA) + add_definitions(-D_GLIBCXX_DEBUG) # Runtime checks for STL containers. + add_cxxflag("-ftrapv") # to add checks for (undefined) signed integer overflow + add_cxxflag("-fcatch-undefined-behavior") # (clang) + add_cxxflag("-fbounds-checking") +else() + # -Wuninitialized causes too many false positives - thanks very much, gcc + add_cxxflag("-Wno-uninitialized") +endif() -list(APPEND CMAKE_CXX_FLAGS "-ggdb -O0 -march=native -Wl,--as-needed") +if(UNITY_BUILD) + add_cxxflag("-fwhole-program") +endif() set(INNOEXTRACT_SOURCES @@ -69,7 +113,58 @@ set(INNOEXTRACT_SOURCES src/InnoExtract.cpp ) +file(GLOB_RECURSE ALL_INCLUDES "${CMAKE_SOURCE_DIR}/src/*.hpp") + include_directories(src) -add_executable(innoextract ${INNOEXTRACT_SOURCES}) +add_executable(innoextract ${INNOEXTRACT_SOURCES} ${ALL_INCLUDES}) target_link_libraries(innoextract ${LIBRARIES}) + +# Additional targets. + +find_package(PythonInterp) + +if(PYTHONINTERP_FOUND) + + unset(STYLE_FILTER) + + # Complains about any c-style cast -> too annoying. + set(STYLE_FILTER ${STYLE_FILTER},-readability/casting) + + # Insists on including evrything in the .cpp file even if it is included in the header. + # This behaviour conflicts with orther tools. + set(STYLE_FILTER ${STYLE_FILTER},-build/include_what_you_use) + + # Too many false positives and not very helpful error messages. + set(STYLE_FILTER ${STYLE_FILTER},-build/include_order) + + # No thanks. + set(STYLE_FILTER ${STYLE_FILTER},-readability/streams) + + # Ugh! + set(STYLE_FILTER ${STYLE_FILTER},-whitespace/tab) + + # Yes it is! + set(STYLE_FILTER ${STYLE_FILTER},-whitespace/blank_line) + + # Suggessts excessive indentation. + set(STYLE_FILTER ${STYLE_FILTER},-whitespace/labels) + + # Don't tell me how to name my variables. + set(STYLE_FILTER ${STYLE_FILTER},-runtime/arrays) + + # Why? + set(STYLE_FILTER ${STYLE_FILTER},-whitespace/todo) + set(STYLE_FILTER ${STYLE_FILTER},-readability/todo) + + # TODO add copyright notices + set(STYLE_FILTER ${STYLE_FILTER},-legal/copyright) + + # TODO split up main() + set(STYLE_FILTER ${STYLE_FILTER},-readability/fn_size) + + add_custom_target(style + COMMAND cmake -E chdir "${CMAKE_SOURCE_DIR}" "${PYTHON_EXECUTABLE}" "${CMAKE_MODULE_PATH}/cpplint.py" "--filter=${STYLE_FILTER}" ${INNOEXTRACT_SOURCES} ${ALL_INCLUDES} + ) + +endif() diff --git a/cmake/CompileCheck.cmake b/cmake/CompileCheck.cmake new file mode 100644 index 0000000..014f66c --- /dev/null +++ b/cmake/CompileCheck.cmake @@ -0,0 +1,106 @@ + +function(check_compiler_flag RESULT FLAG) + + if(DEFINED CHECK_COMPILER_FLAG_${FLAG}) + if(CHECK_COMPILER_FLAG_${FLAG}) + set(${RESULT} "${FLAG}" PARENT_SCOPE) + else() + set(${RESULT} "" PARENT_SCOPE) + endif() + return() + endif() + + set(compile_test_file "${CMAKE_CURRENT_BINARY_DIR}/compile_flag_test.cpp") + file(WRITE ${compile_test_file} "__attribute__((const)) int main(){ return 0; }\n") + try_compile(CHECK_COMPILER_FLAG ${CMAKE_BINARY_DIR} ${compile_test_file} COMPILE_DEFINITIONS "${FLAG}" OUTPUT_VARIABLE ERRORLOG) + + string(REGEX MATCH "warning:" HAS_WARNING "${ERRORLOG}") + + if(NOT CHECK_COMPILER_FLAG) + message(STATUS "Checking compiler flag: ${FLAG} - unsupported") + set(${RESULT} "" PARENT_SCOPE) + set("CHECK_COMPILER_FLAG_${FLAG}" 0 CACHE INTERNAL "...") + elseif(NOT HAS_WARNING STREQUAL "") + message(STATUS "Checking compiler flag: ${FLAG} - unsupported (warning)") + set(${RESULT} "" PARENT_SCOPE) + set("CHECK_COMPILER_FLAG_${FLAG}" 0 CACHE INTERNAL "...") + else() + message(STATUS "Checking compiler flag: ${FLAG}") + set(${RESULT} "${FLAG}" PARENT_SCOPE) + set("CHECK_COMPILER_FLAG_${FLAG}" 1 CACHE INTERNAL "...") + endif() + +endfunction(check_compiler_flag) + +function(add_cxxflag FLAG) + + check_compiler_flag(RESULT "${FLAG}") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RESULT}" PARENT_SCOPE) + +endfunction(add_cxxflag) + +function(try_link_library LIBRARY_NAME LIBRARY_FILE ERROR_VAR) + # See if we can link a simple program with the library using the configured c++ compiler. + set(link_test_file "${CMAKE_CURRENT_BINARY_DIR}/link_test.cpp") + file(WRITE ${link_test_file} "int main(){}\n") + if(CMAKE_THREAD_LIBS_INIT) + list(APPEND LIBRARY_FILE "${CMAKE_THREAD_LIBS_INIT}") + endif() + try_compile(CHECK_${LIBRARY_NAME}_LINK "${CMAKE_BINARY_DIR}" "${link_test_file}" CMAKE_FLAGS "-DLINK_LIBRARIES=${LIBRARY_FILE}" OUTPUT_VARIABLE ERRORLOG) + set(${ERROR_VAR} "${ERRORLOG}" PARENT_SCOPE) +endfunction(try_link_library) + +############################################################################## +# Check that a a library actually works for the current configuration. +function(check_link_library LIBRARY_NAME LIBRARY_VARIABLE) + + set(lib_current "${${LIBRARY_VARIABLE}}") + set(found_var "ARX_CLL_${LIBRARY_NAME}_FOUND") + set(working_var "ARX_CLL_${LIBRARY_NAME}_WORKING") + + if(CHECK_${LIBRARY_NAME}_LINK) + set(lib_found "${${found_var}}") + set(lib_working "${${working_var}}") + if((lib_current STREQUAL lib_found) OR (lib_current STREQUAL lib_working)) + set("${LIBRARY_VARIABLE}" "${lib_working}" PARENT_SCOPE) + return() + endif() + endif() + + set("${found_var}" "${lib_current}" CACHE INTERNAL "...") + + message(STATUS "Checking ${LIBRARY_NAME}: ${lib_current}") + + # Check if we can link to the full path found by find_package. + try_link_library(${LIBRARY_NAME} "${lib_current}" ERRORLOG1) + + if(CHECK_${LIBRARY_NAME}_LINK) + set("${working_var}" "${lib_current}" CACHE INTERNAL "...") + return() + endif() + + # Check if the linker is smarter than cmake and try to link with only the library name. + string(REGEX REPLACE "(^|;)[^;]*/lib([^;/]*)\\.so" "\\1-l\\2" LIBRARY_FILE "${lib_current}") + try_link_library(${LIBRARY_NAME} "${LIBRARY_FILE}" ERRORLOG2) + + if(CHECK_${LIBRARY_NAME}_LINK) + message(STATUS " -> using ${LIBRARY_FILE} instead") + set("${LIBRARY_VARIABLE}" "${LIBRARY_FILE}" PARENT_SCOPE) + set("${working_var}" "${LIBRARY_FILE}" CACHE INTERNAL "...") + return() + endif() + + # Force cmake to search again, as the cached library doesn't work. + unset(FIND_PACKAGE_MESSAGE_DETAILS_${ARGV2} CACHE) + unset(FIND_PACKAGE_MESSAGE_DETAILS_${LIBRARY_NAME} CACHE) + + message(FATAL_ERROR "\n${ERRORLOG1}\n\n${ERRORLOG2}\n\n!! No suitable (32- vs. 64-bit) version of ${LIBRARY_NAME} found; tried ${lib_current} and ${LIBRARY_FILE}\nusing compiler ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS}\n") + +endfunction(check_link_library) + +function(force_recheck_library LIBRARY_NAME) + unset(FIND_PACKAGE_MESSAGE_DETAILS_${ARGV1} CACHE) + unset(FIND_PACKAGE_MESSAGE_DETAILS_${LIBRARY_NAME} CACHE) + unset(CHECK_${LIBRARY_NAME}_LINK CACHE) +endfunction() diff --git a/cmake/FindLZMA.cmake b/cmake/FindLZMA.cmake new file mode 100644 index 0000000..f9459b1 --- /dev/null +++ b/cmake/FindLZMA.cmake @@ -0,0 +1,22 @@ + +# Try to find the LZMA library and include path for lzma.h from xz-utils. +# Once done this will define +# +# LZMA_FOUND +# LZMA_INCLUDE_DIR - where to find lzma.h +# LZMA_LIBRARIES - liblzma.so + +find_path(LZMA_INCLUDE_DIR lzma.h DOC "The directory where lzma.h resides") +find_library(LZMA_LIBRARY lzma DOC "The LZMA library") + +mark_as_advanced(LZMA_INCLUDE_DIR) +mark_as_advanced(LZMA_LIBRARY) + +# handle the QUIETLY and REQUIRED arguments and set LZMA_FOUND to TRUE if +# all listed variables are TRUE +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LZMA DEFAULT_MSG LZMA_LIBRARY LZMA_INCLUDE_DIR) + +if(LZMA_FOUND) + set(LZMA_LIBRARIES ${LZMA_LIBRARY}) +endif(LZMA_FOUND) diff --git a/cmake/VersionScript.cmake b/cmake/VersionScript.cmake new file mode 100644 index 0000000..a38bd44 --- /dev/null +++ b/cmake/VersionScript.cmake @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.8) + +# CMake script that reads a VERSION file and the current git history and the calls configure_file(). +# This is used by version_file() in VersionString.cmake + +if((NOT DEFINED INPUT) OR (NOT DEFINED OUTPUT) OR (NOT DEFINED VERSION_FILE) OR (NOT DEFINED GIT_DIR)) + message(SEND_ERROR "Invalid arguments.") +endif() + +file(READ "${VERSION_FILE}" BASE_VERSION) +string(STRIP "${BASE_VERSION}" BASE_VERSION) + +if(EXISTS "${GIT_DIR}") + + file(READ "${GIT_DIR}/HEAD" GIT_HEAD) + string(STRIP "${GIT_HEAD}" GIT_HEAD) + + unset(GIT_COMMIT) + + if("${GIT_HEAD}" MATCHES "^ref\\:") + + # Remove the first for characters from GIT_HEAD to get GIT_REF. + # We can't use a length of -1 for string(SUBSTRING) as cmake < 2.8.5 doesn't support it. + string(LENGTH "${GIT_HEAD}" GIT_HEAD_LENGTH) + math(EXPR GIT_REF_LENGTH "${GIT_HEAD_LENGTH} - 4") + string(SUBSTRING "${GIT_HEAD}" 4 ${GIT_REF_LENGTH} GIT_REF) + + string(STRIP "${GIT_REF}" GIT_REF) + + file(READ "${GIT_DIR}/${GIT_REF}" GIT_HEAD) + string(STRIP "${GIT_HEAD}" GIT_HEAD) + endif() + + string(REGEX MATCH "[0-9A-Za-z]+" GIT_COMMIT "${GIT_HEAD}") + + # Create variables for all prefixes of the git comit ID. + if(GIT_COMMIT) + string(TOLOWER "${GIT_COMMIT}" GIT_COMMIT) + string(LENGTH "${GIT_COMMIT}" GIT_COMMIT_LENGTH) + foreach(i RANGE "${GIT_COMMIT_LENGTH}") + string(SUBSTRING "${GIT_COMMIT}" 0 ${i} GIT_COMMIT_PREFIX_${i}) + endforeach() + endif() + +endif() + +configure_file("${INPUT}" "${OUTPUT}" ESCAPE_QUOTES) diff --git a/cmake/VersionString.cmake b/cmake/VersionString.cmake new file mode 100644 index 0000000..ab70b10 --- /dev/null +++ b/cmake/VersionString.cmake @@ -0,0 +1,41 @@ + +# Create a rule to generate a version string at compile time. +# +# SRC is processed using the configure_file() cmake command +# at build to produce DST with the following variable available: +# +# - BASE_VERSION: The contents of the file specified by VERSION_FILE. +# - GIT_COMMIT: The current git commit. This variable is not defined if there is no GIT_DIR directory. +# - SHORT_GIT_COMMIT: The first 10 characters of the git commit. +# For the exact syntax of SRC see the documentation of the configure_file() cmake command. +# +# The version file is regenerated whenever VERSION_FILE or the current commit changes. +function(version_file SRC DST VERSION_FILE GIT_DIR) + + get_filename_component(ABS_SRC "${SRC}" ABSOLUTE) + get_filename_component(ABS_DST "${DST}" ABSOLUTE) + get_filename_component(ABS_VERSION_FILE "${VERSION_FILE}" ABSOLUTE) + get_filename_component(ABS_GIT_DIR "${GIT_DIR}" ABSOLUTE) + + add_custom_command( + OUTPUT + "${DST}" + COMMAND + ${CMAKE_COMMAND} + "-DINPUT=${ABS_SRC}" + "-DOUTPUT=${ABS_DST}" + "-DVERSION_FILE=${ABS_VERSION_FILE}" + "-DGIT_DIR=${ABS_GIT_DIR}" + -P "${CMAKE_MODULE_PATH}/VersionScript.cmake" + MAIN_DEPENDENCY + "${SRC}" + DEPENDS + "${GIT_DIR}/HEAD" + "${GIT_DIR}/logs/HEAD" + "${VERSION_FILE}" + "${CMAKE_MODULE_PATH}/VersionScript.cmake" + COMMENT "" + VERBATIM + ) + +endfunction(version_file) diff --git a/cmake/clang-toolchain.cmake b/cmake/clang-toolchain.cmake new file mode 100644 index 0000000..2b5d29e --- /dev/null +++ b/cmake/clang-toolchain.cmake @@ -0,0 +1,14 @@ + +# Look for wine compilers +find_program(Clang clang) +mark_as_advanced(Clang) +find_program(ClangXX clang++) +mark_as_advanced(ClangXX) + +if((NOT Clang) OR (NOT ClangXX)) + message(FATAL_ERROR "clang not found (found: c compiler \"${Clang}\", c++ compiler \"${ClangXX}\")") +endif() + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${Clang}") +set(CMAKE_CXX_COMPILER "${ClangXX}") diff --git a/cmake/cpplint.py b/cmake/cpplint.py new file mode 100644 index 0000000..d742f6e --- /dev/null +++ b/cmake/cpplint.py @@ -0,0 +1,3153 @@ +#!/usr/bin/python2.4 +# +# Note: this file has been adjusted to fit InnoExtract: +# - adjusted include guard style +# - hacked so that build/include doesn't complain about #include "Configure.h" lines +# - Allow lines that are only whitespace. +# - Remove 80-char line limit, keep 100 char limit. +# - No space after if et al. +# - Warn if a tab follows a non-tab character. +# - Don't require two spaces between code and comments +# - Warn if spaces are used for identation. +# - Allow //! comments +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Here are some issues that I've had people identify in my code during reviews, +# that I think are possible to flag automatically in a lint tool. If these were +# caught by lint, it would save time both for myself and that of my reviewers. +# Most likely, some of these are beyond the scope of the current lint framework, +# but I think it is valuable to retain these wish-list items even if they cannot +# be immediately implemented. +# +# Suggestions +# ----------- +# - Check for no 'explicit' for multi-arg ctor +# - Check for boolean assign RHS in parens +# - Check for ctor initializer-list colon position and spacing +# - Check that if there's a ctor, there should be a dtor +# - Check accessors that return non-pointer member variables are +# declared const +# - Check accessors that return non-const pointer member vars are +# *not* declared const +# - Check for using public includes for testing +# - Check for spaces between brackets in one-line inline method +# - Check for no assert() +# - Check for spaces surrounding operators +# - Check for 0 in pointer context (should be NULL) +# - Check for 0 in char context (should be '\0') +# - Check for camel-case method name conventions for methods +# that are not simple inline getters and setters +# - Check that base classes have virtual destructors +# put " // namespace" after } that closes a namespace, with +# namespace's name after 'namespace' if it is named. +# - Do not indent namespace contents +# - Avoid inlining non-trivial constructors in header files +# include base/basictypes.h if DISALLOW_EVIL_CONSTRUCTORS is used +# - Check for old-school (void) cast for call-sites of functions +# ignored return value +# - Check gUnit usage of anonymous namespace +# - Check for class declaration order (typedefs, consts, enums, +# ctor(s?), dtor, friend declarations, methods, member vars) +# + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] + [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +# \ used for clearer layout -- pylint: disable-msg=C6013 +_ERROR_CATEGORIES = [ + 'build/class', + 'build/deprecated', + 'build/endif_comment', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/nolint', + 'readability/streams', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/rtti', + 'runtime/sizeof', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/virtual', + 'whitespace/align_tab' + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/indent', + 'whitespace/ident_space', + 'whitespace/labels', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo' + ] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = [ '-build/include_alpha' ] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a seperate i18n file. + +# Headers that we consider STL headers. +_STL_HEADERS = frozenset([ + 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', + 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', + 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', + 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', + 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', + 'utility', 'vector', 'vector.h', + ]) + + +# Non-STL C++ system headers. +_CPP_HEADERS = frozenset([ + 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', + 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', + 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', + 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', + 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', + 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', + 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream.h', + 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', + 'numeric', 'ostream.h', 'parsestream.h', 'pfstream.h', 'PlotFile.h', + 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'ropeimpl.h', + 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', + 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string', + 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', + ]) + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + m = _RE_SUPPRESSION.search(raw_line) + if m: + category = m.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparisson. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + header_path: Header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) + if self._last_header > canonical_header: + return False + self._last_header = canonical_header + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analizing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo: + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN? Try to find a git or hg top level directory by searching up + # from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + os.path.basename(root_dir) != "src" and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.basename(root_dir) == "src" or + os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """Returns true iff confidence >= verbose, category passes + filter and is not NOLINT-suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos] + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 3 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw member contains all the lines without processing. + All these three members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + for linenum in range(len(lines)): + self.lines.append(CleanseComments(lines[linenum])) + elided = self._CollapseStrings(lines[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in '({[': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': endchar = ')' + if startchar == '[': endchar = ']' + if startchar == '{': endchar = '}' + + num_open = line.count(startchar) - line.count(endchar) + while linenum < clean_lines.NumLines() and num_open > 0: + linenum += 1 + line = clean_lines.elided[linenum] + num_open += line.count(startchar) - line.count(endchar) + # OK, now find the endchar that actually got us back to even + endpos = len(line) + while num_open >= 0: + endpos = line.rfind(')', 0, endpos) + num_open -= 1 # chopped off another ) + return (line, linenum, endpos + 1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] "') + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + + fileinfo = FileInfo(filename) + return "ARX_" + re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef or not define or ifndef != define: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForUnicodeReplacementCharacters(filename, lines, error): + """Logs an error for each line containing Unicode replacement characters. + + These indicate that either the file contained invalid UTF-8 (likely) + or Unicode replacement characters (which it shouldn't). Note that + it's possible for this to throw off line numbering if the invalid + UTF-8 occurred adjacent to a newline. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. They\'re ' + 'ugly and unnecessary, and you should use concatenation instead".') + + +threading_list = ( + ('asctime(', 'asctime_r('), + ('ctime(', 'ctime_r('), + ('getgrgid(', 'getgrgid_r('), + ('getgrnam(', 'getgrnam_r('), + ('getlogin(', 'getlogin_r('), + ('getpwnam(', 'getpwnam_r('), + ('getpwuid(', 'getpwuid_r('), + ('gmtime(', 'gmtime_r('), + ('localtime(', 'localtime_r('), + ('rand(', 'rand_r('), + ('readdir(', 'readdir_r('), + ('strtok(', 'strtok_r('), + ('ttyname(', 'ttyname_r('), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...) for improved thread safety.') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +class _ClassInfo(object): + """Stores information about a class.""" + + def __init__(self, name, linenum): + self.name = name + self.linenum = linenum + self.seen_open_brace = False + self.is_derived = False + self.virtual_method_linenumber = None + self.has_virtual_destructor = False + self.brace_depth = 0 + + +class _ClassState(object): + """Holds the current state of the parse relating to class declarations. + + It maintains a stack of _ClassInfos representing the parser's guess + as to the current nesting of class declarations. The innermost class + is at the top (back) of the stack. Typically, the stack will either + be empty or have exactly one entry. + """ + + def __init__(self): + self.classinfo_stack = [] + + def CheckFinished(self, filename, error): + """Checks that all classes have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + if self.classinfo_stack: + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + error(filename, self.classinfo_stack[0].linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + self.classinfo_stack[0].name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + class_state, error): + """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Track class entry and exit, and attempt to find cases within the + # class declaration that don't meet the C++ style + # guidelines. Tracking is very dependent on the code matching Google + # style guidelines, but it seems to perform well enough in testing + # to be a worthwhile addition to the checks. + classinfo_stack = class_state.classinfo_stack + # Look for a class declaration + class_decl_match = Match( + r'\s*(template\s*<[\w\s<>,:]*>\s*)?(class|struct)\s+(\w+(::\w+)*)', line) + if class_decl_match: + classinfo_stack.append(_ClassInfo(class_decl_match.group(3), linenum)) + + # Everything else in this function uses the top of the stack if it's + # not empty. + if not classinfo_stack: + return + + classinfo = classinfo_stack[-1] + + # If the opening brace hasn't been seen look for it and also + # parent class declarations. + if not classinfo.seen_open_brace: + # If the line has a ';' in it, assume it's a forward declaration or + # a single-line class declaration, which we won't process. + if line.find(';') != -1: + classinfo_stack.pop() + return + classinfo.seen_open_brace = (line.find('{') != -1) + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', line): + classinfo.is_derived = True + if not classinfo.seen_open_brace: + return # Everything else in this function is for after open brace + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + args = Match(r'(? 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + +def CheckSpacing(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't have too many + blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + raw = clean_lines.raw_lines + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + if IsBlankLine(line): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if (prevbrace != -1 and prev_line[prevbrace:].find('}') == -1 + and prev_line[:prevbrace].find('namespace') == -1): + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the paramters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Blank line at the start of a code block. Is this needed?') + # This doesn't ignore whitespace at the end of a namespace block + # because that is too hard without pairing open/close braces; + # however, a special exception is made for namespace closing + # brackets which have a comment containing "namespace". + # + # Also, ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('namespace') == -1 + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Blank line at the end of a code block. Is this needed?') + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + #if (not Match(r'^\s*{ //', line) and + # ((commentpos >= 1 and + # line[commentpos-1] not in string.whitespace) or + # (commentpos >= 2 and + # line[commentpos-2] not in string.whitespace))): + # error(filename, linenum, 'whitespace/comments', 2, + # 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^! ', line[commentend:]) or + Search(r'^!< ', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # Alas, we can't test < or > because they're legitimately used sans spaces + # (a->b, vector a). The only time we can tell is a < with no >, and + # only if it's not template params list spilling into the next line. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if not match: + # Note that while it seems that the '<[^<]*' term in the following + # regexp could be simplified to '<.*', which would indeed match + # the same class of strings, the [^<] means that searching for the + # regexp takes linear rather than quadratic time. + if not Search(r'<[^<]*,\s*$', line): # template params spill + match = Search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + # We allow no-spaces around << and >> when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + match = Search(r'[^0-9\s](<<|>>)[^0-9\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\s\(|for\s\(|while\s\(|switch\s\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Extra space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if not len(match.group(2)) in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or operator) + if Search(r',[^\s]', line): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Except after an opening paren, you should have spaces before your braces. + # And since you should never have braces at the beginning of a line, this is + # an easy test. + if Search(r'[^ (\t]{', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use { } instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use { } instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use { } instead.') + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, + # which is commonly used to control the lifetime of + # stack-allocated variables. We don't detect this perfectly: we + # just don't complain if the last non-whitespace character on the + # previous non-blank line is ';', ':', '{', or '}'. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if not Search(r'[;:}{]\s*$', prevline): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Braces shouldn't be followed by a ; unless they're defining a struct + # or initializing an array. + # We can't tell in general, but we can for some common cases. + prevlinenum = linenum + while True: + (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) + if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): + line = prevline + line + else: + break + if (Search(r'{.*}\s*;', line) and + line.count('{') == line.count('}') and + not Search(r'struct|class|enum|\s*=\s*{', line)): + error(filename, linenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def ReplaceableCheck(operator, macro, line): + """Determine whether a basic CHECK can be replaced with a more specific one. + + For example suggest using CHECK_EQ instead of CHECK(a == b) and + similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. + + Args: + operator: The C++ operator used in the CHECK. + macro: The CHECK or EXPECT macro being called. + line: The current source line. + + Returns: + True if the CHECK can be replaced with a more specific one. + """ + + # This matches decimal and hex integers, strings, and chars (in that order). + match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' + + # Expression to match two sides of the operator with something that + # looks like a literal, since CHECK(x == iterator) won't compile. + # This means we can't catch all the cases where a more specific + # CHECK is possible, but it's less annoying than dealing with + # extraneous warnings. + match_this = (r'\s*' + macro + r'\((\s*' + + match_constant + r'\s*' + operator + r'[^<>].*|' + r'.*[^<>]' + operator + r'\s*' + match_constant + + r'\s*\))') + + # Don't complain about CHECK(x == NULL) or similar because + # CHECK_EQ(x, NULL) won't compile (requires a cast). + # Also, don't complain about more complex boolean expressions + # involving && or || such as CHECK(a == b || c == d). + return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + raw_lines = clean_lines.raw_lines + current_macro = '' + for macro in _CHECK_MACROS: + if raw_lines[linenum].find(macro) >= 0: + current_macro = macro + break + if not current_macro: + # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + return + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. + for operator in ['==', '!=', '>=', '>', '<=', '<']: + if ReplaceableCheck(operator, current_macro, line): + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[current_macro][operator], + current_macro, operator)) + break + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for c in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(c) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(c): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + error: The function to call with any errors found. + """ + + raw_lines = clean_lines.raw_lines + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + if linenum > 0: + last_line = raw_lines[linenum - 1] + lasttabs = 0 + while lasttabs < len(last_line) and last_line[lasttabs] == '\t': + lasttabs += 1 + for char in line: + if not char.isspace(): + break + if lasttabs == 0 and char != '\t': + break + if lasttabs == -1: + if char == '\t' and last_line != '': + error(filename, linenum, 'whitespace/align_tab', 4, + 'Too much indentation or tab used as alignment.') + break + if lasttabs > 0 and char != '\t': + error(filename, linenum, 'whitespace/ident_space', 4, + 'Space used for identation, use tabs instead.') + break + lasttabs -= 1 + + foundntab = 0 + for char in line: + if char != '\t': + foundntab = 1 + if foundntab and char == '\t': + error(filename, linenum, 'whitespace/align_tab', 4, + 'Tab used for alignment, use spaces instead.') + break + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + cleansed_line = clean_lines.elided[linenum] + if line and line[-1].isspace() and not line.isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line)): + line_width = GetLineWidth(line) + if line_width > 100: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than 100 characters') + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 4, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckSpacing(filename, clean_lines, linenum, error) + CheckCheck(filename, clean_lines, linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input filename. + + Returns: + True if 'filename' looks like a test, False otherwise. + """ + if (filename.endswith('_test.cc') or + filename.endswith('_unittest.cc') or + filename.endswith('_regtest.cc')): + return True + else: + return False + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_stl_h = include in _STL_HEADERS + is_cpp_h = is_stl_h or include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line) and line != "#include \"Configure.h\"": + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + if not include_state.IsInAlphabeticalOrder(include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + + # Look for any of the stream classes that are part of standard C++. + match = _RE_PATTERN_INCLUDE.match(line) + if match: + include = match.group(2) + if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): + # Many unit tests use cout, so we exempt them. + if not _IsTestFilename(filename): + error(filename, linenum, 'readability/streams', 3, + 'Streams are highly discouraged.') + +def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, + error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Create an extended_line, which is the concatenation of the current and + # next lines, for more effective checking of code that may span more than one + # line. + if linenum + 1 < clean_lines.NumLines(): + extended_line = line + clean_lines.elided[linenum + 1] + else: + extended_line = line + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check for non-const references in functions. This is tricky because & + # is also used to take the address of something. We allow <> for templates, + # (ignoring whatever is between the braces) and : for classes. + # These are complicated re's. They try to capture the following: + # paren (for fn-prototype start), typename, &, varname. For the const + # version, we're willing for const to be before typename or after + # Don't check the implemention on same line. + fnline = line.split('{', 1)[0] + if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > + len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' + r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', + fnline))): + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". + if not Search( + r'(swap|Swap|operator[<>][<>])\s*\(\s*(?:[\w:]|<.*>)+\s*&', + fnline): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer.') + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there + r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + if match: + # gMock methods are defined using some variant of MOCK_METHODx(name, type) + # where type may be float(), int(string), etc. Without context they are + # virtually indistinguishable from int(x) casts. + if (match.group(1) is None and # If new operator, then this isn't a cast + not Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line)): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + match.group(2)) + + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', + error) + # This doesn't catch all cases. Consider (const char * const)"hello". + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + if Search( + r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + # Make sure it's not a function. + # Function template specialization looks like: "string foo(...". + # Class template definitions look like: "string Foo::Method(...". + if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', + match.group(3)): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + # Check that we're not using RTTI outside of testing code. + if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): + error(filename, linenum, 'runtime/rtti', 5, + 'Do not use dynamic_cast<>. If you need to cast within a class ' + "hierarchy, use static_cast<> to upcast. Google doesn't support " + 'RTTI.') + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + if Search(r'\bsscanf\b', line): + error(filename, linenum, 'runtime/printf', 1, + 'sscanf can be ok, but is slow and can overflow buffers.') + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + match = re.search(r'\b((?:string)?printf)\s*\(([\w.\->()]+)\)', line, re.I) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (match.group(1), match.group(2))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token becasue we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or + # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing + # in the class declaration. + match = Match( + (r'\s*' + r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' + r'\(.*\);$'), + line) + if match and linenum + 1 < clean_lines.NumLines(): + next_line = clean_lines.elided[linenum + 1] + if not Search(r'^\s*};', next_line): + error(filename, linenum, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, + error): + """Checks for a C-style cast by looking for the pattern. + + This also handles sizeof(type) warnings, due to similarity of content. + + Args: + filename: The name of the current file. + linenum: The number of the line to check. + line: The line of code to check. + raw_line: The raw line of code to check, with comments. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast or static_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + """ + match = Search(pattern, line) + if not match: + return + + # e.g., sizeof(int) + sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) + if sizeof_match: + error(filename, linenum, 'runtime/sizeof', 1, + 'Using sizeof(type). Use sizeof(varname) instead if possible') + return + + remainder = line[match.end(0):] + + # The close paren is for function pointers as arguments to a function. + # eg, void foo(void (*bar)(int)); + # The semicolon check is a more basic function check; also possibly a + # function pointer typedef. + # eg, void foo(int); or void foo(int) const; + # The equals check is for function pointer assignment. + # eg, void *(*foo)(int) = ... + # + # Right now, this will only catch cases where there's a single argument, and + # it's unnamed. It should probably be expanded to check for multiple + # arguments with some unnamed. + function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)))', remainder) + if function_match: + if (not function_match.group(3) or + function_match.group(3) == ';' or + raw_line.find('/*') < 0): + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator',)), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_HEADERS_ACCEPTED_BUT_NOT_PROMOTED = { + # We can trust with reasonable confidence that map gives us pair<>, too. + 'pair<>': ('map', 'multimap', 'hash_map', 'hash_multimap') +} + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_algorithm_header = [] +for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', + 'transform'): + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '')) + +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + if not filename_cc.endswith('.cc'): + return (False, '') + filename_cc = filename_cc[:-len('.cc')] + if filename_cc.endswith('_unittest'): + filename_cc = filename_cc[:-len('_unittest')] + elif filename_cc.endswith('_test'): + filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_state, io=codecs): + """Fill up the include_state with new includes found from the file. + + Args: + filename: the name of the header to read. + include_state: an _IncludeState instance in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was succesfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + # The value formatting is cute, but not really used right now. + # What matters here is that the key is in include_state. + include_state.setdefault(include, '%s:%d' % (filename, linenum)) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + m = _RE_PATTERN_STRING.search(line) + if m: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:m.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_algorithm_header: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's copy the include_state so it is only messed up within this function. + include_state = include_state.copy() + + # Did we find the header for this file (if any) and succesfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = os.path.abspath(filename) + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_state is modified during iteration, so we iterate over a copy of + # the keys. + for header in include_state.keys(): #NOLINT + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_state, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + + # All the lines have been processed, report the errors found. + for required_header_unstripped in required: + template = required[required_header_unstripped][1] + if template in _HEADERS_ACCEPTED_BUT_NOT_PROMOTED: + headers = _HEADERS_ACCEPTED_BUT_NOT_PROMOTED[template] + if [True for header in headers if header in include_state]: + continue + if required_header_unstripped.strip('<>"') not in include_state: + error(filename, required[required_header_unstripped][0], + 'build/include_what_you_use', 4, + 'Add #include ' + required_header_unstripped + ' for ' + template) + + +def ProcessLine(filename, file_extension, + clean_lines, line, include_state, function_state, + class_state, error): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + class_state: A _ClassState instance which maintains information about + the current stack of nested class declarations being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + error) + CheckForNonStandardConstructs(filename, clean_lines, line, + class_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + + +def ProcessFileData(filename, file_extension, lines, error): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is termined with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + class_state = _ClassState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, class_state, error) + class_state.CheckFinished(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForUnicodeReplacementCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessFile(filename, vlevel): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if (filename != '-' and file_extension != 'hpp' and file_extension != 'cpp'): + sys.stderr.write('Ignoring %s; not a .cpp or .hpp file\n' % filename) + else: + ProcessFileData(filename, file_extension, lines, Error) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputing only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + # dennda: + # sys.stderr.write('Done processing %s\n' % filename) + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if not val in ('emacs', 'vs7'): + PrintUsage('The only allowed output formats are emacs and vs7.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/cmake/ekopath-toolchain.cmake b/cmake/ekopath-toolchain.cmake new file mode 100644 index 0000000..116e41d --- /dev/null +++ b/cmake/ekopath-toolchain.cmake @@ -0,0 +1,18 @@ + +# Look for wine compilers +find_program(Ekopath pathcc) +mark_as_advanced(Ekopath) +find_program(EkopathXX pathCC) +mark_as_advanced(EkopathXX) + +if((NOT Ekopath) OR (NOT EkopathXX)) + message(FATAL_ERROR "ekopath not found (found: c compiler \"${Ekopath}\", c++ compiler \"${EkopathXX}\")") +endif() + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${Ekopath}") +set(CMAKE_CXX_COMPILER "${EkopathXX}") + +# tell cmake that ekopath supports -isystem +set(CMAKE_INCLUDE_SYSTEM_FLAG_C "-isystem ") +set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-isystem ") diff --git a/cmake/mingw32-toolchain.cmake b/cmake/mingw32-toolchain.cmake new file mode 100644 index 0000000..c75ed7a --- /dev/null +++ b/cmake/mingw32-toolchain.cmake @@ -0,0 +1,78 @@ + +# the name of the target operating system +set(CMAKE_SYSTEM_NAME Windows) + +# Look for mingw32 compilers +if(DEFINED MINGW32_ROOT) + set(MinGW32_ROOT "${MINGW32_ROOT}" CACHE INTERNAL) +else() + find_path(MinGW32_ROOT mingw + PATH_SUFFIXES + i686-pc-mingw32 + i586-pc-mingw32 + i486-pc-mingw32 + i386-pc-mingw32 + i686-mingw32 + i586-mingw32 + i486-mingw32 + i386-mingw32 + mingw32 + PATHS + /usr + /usr/local + ) +endif() +mark_as_advanced(MinGW32_ROOT) + +find_program(MinGW32_GCC NAMES + i686-pc-mingw32-gcc + i586-pc-mingw32-gcc + i486-pc-mingw32-gcc + i386-pc-mingw32-gcc + i686-mingw32-gcc + i586-mingw32-gcc + i486-mingw32-gcc + i386-mingw32-gcc +) +mark_as_advanced(MinGW32_GCC) +find_program(MinGW32_GXX NAMES + i686-pc-mingw32-g++ + i586-pc-mingw32-g++ + i486-pc-mingw32-g++ + i386-pc-mingw32-g++ + i686-mingw32-g++ + i586-mingw32-g++ + i486-mingw32-g++ + i386-mingw32-g++ +) +mark_as_advanced(MinGW32_GXX) +find_program(MinGW32_RC NAMES + i686-pc-mingw32-windres + i586-pc-mingw32-windres + i486-pc-mingw32-windres + i386-pc-mingw32-windres + i686-mingw32-windres + i586-mingw32-windres + i486-mingw32-windres + i386-mingw32-windres +) +mark_as_advanced(MinGW32_RC) + +if((NOT MinGW32_GCC) OR (NOT MinGW32_GXX) OR (NOT MinGW32_RC) OR (NOT MinGW32_ROOT)) + message(FATAL_ERROR "mingw32 not found (found gcc=\"${MinGW32_GCC}\", g++=\"${MinGW32_GXX}\" rc=\"${MinGW32_RC}\" root=\"${MinGW32_ROOT}\")") +endif() + +# which compilers to use for C and C++ +set(CMAKE_C_COMPILER "${MinGW32_GCC}") +set(CMAKE_CXX_COMPILER "${MinGW32_GXX}") +set(CMAKE_RC_COMPILER "${MinGW32_RC}") + +# here is the target environment located +set(CMAKE_FIND_ROOT_PATH "${MinGW32_ROOT}") + +# adjust the default behaviour of the find_xxx() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY FIRST) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE FIRST) diff --git a/src/InnoExtract.cpp b/src/InnoExtract.cpp index 4416a0b..9fdceac 100644 --- a/src/InnoExtract.cpp +++ b/src/InnoExtract.cpp @@ -77,7 +77,7 @@ public: if(progress_length > 10) { - size_t progress = ceil(progress_length * value); + size_t progress = size_t(ceil(float(progress_length) * value)); std::cout << '['; for(size_t i = 0; i < progress; i++) { @@ -91,7 +91,8 @@ public: } - std::cout << std::right << std::fixed << std::setprecision(1) << std::setfill(' ') << std::setw(5) << (value * 100) << "% " << label; + std::cout << std::right << std::fixed << std::setprecision(1) << std::setfill(' ') + << std::setw(5) << (value * 100) << "% " << label; std::cout.flush(); std::cout.flags(flags); @@ -99,7 +100,7 @@ public: } static void clear() { - std::cout << "\33[2K\r" ; + std::cout << "\33[2K\r"; } }; @@ -119,9 +120,9 @@ void discard(T & is, uint64_t bytes) { char buf[1024]; while(bytes) { - size_t n = std::min(bytes, ARRAY_SIZE(buf)); + std::streamsize n = std::streamsize(std::min(bytes, ARRAY_SIZE(buf))); is.read(buf, n); - bytes -= n; + bytes -= uint64_t(n); } } @@ -133,7 +134,7 @@ struct FileLocationComparer { const std::vector & locations; - FileLocationComparer(const std::vector & loc) : locations(loc) { } + explicit FileLocationComparer(const std::vector & loc) : locations(loc) { } FileLocationComparer(const FileLocationComparer & o) : locations(o.locations) { } bool operator()(size_t a, size_t b) { @@ -142,7 +143,8 @@ struct FileLocationComparer { }; -void printSetupItem(std::ostream & os, const SetupItem & item, const SetupHeader & header) { +static void printSetupItem(std::ostream & os, const SetupItem & item, + const SetupHeader & header) { os << IfNotEmpty(" Componenets", item.components); os << IfNotEmpty(" Tasks", item.tasks); @@ -157,7 +159,7 @@ void printSetupItem(std::ostream & os, const SetupItem & item, const SetupHeader } -void print(std::ostream & os, const RunEntry & entry, const SetupHeader & header) { +static void print(std::ostream & os, const RunEntry & entry, const SetupHeader & header) { os << " - " << Quoted(entry.name) << ':' << endl; os << IfNotEmpty(" Parameters", entry.parameters); @@ -176,7 +178,7 @@ void print(std::ostream & os, const RunEntry & entry, const SetupHeader & header } -std::ostream & operator<<(std::ostream & os, const Checksum & checksum) { +static std::ostream & operator<<(std::ostream & os, const Checksum & checksum) { std::ios_base::fmtflags old = os.flags(); @@ -220,7 +222,7 @@ static const char * magicNumbers[][2] = { { "BM", "bmp" }, }; -const char * guessExtension(const string & data) { +static const char * guessExtension(const string & data) { for(size_t i = 0; i < ARRAY_SIZE(magicNumbers); i++) { @@ -234,13 +236,14 @@ const char * guessExtension(const string & data) { return "bin"; } -void dump(std::istream & is, const string & file) { +static void dump(std::istream & is, const string & file) { // TODO stream std::string data; is >> BinaryString(data); - cout << "Resource: " << color::cyan << file << color::reset << ": " << color::white << data.length() << color::reset << " bytes" << endl; + cout << "Resource: " << color::cyan << file << color::reset << ": " << color::white + << data.length() << color::reset << " bytes" << endl; if(data.empty()) { return; @@ -248,12 +251,14 @@ void dump(std::istream & is, const string & file) { std::string filename = file + '.' + guessExtension(data); - std::ofstream ofs(filename.c_str(), std::ios_base::trunc | std::ios_base::binary | std::ios_base::out); + std::ofstream ofs(filename.c_str(), std::ios_base::trunc | std::ios_base::binary + | std::ios_base::out); ofs << data; }; -void readWizardImageAndDecompressor(std::istream & is, const InnoVersion & version, const SetupHeader & header) { +static void readWizardImageAndDecompressor(std::istream & is, const InnoVersion & version, + const SetupHeader & header) { cout << endl; @@ -264,8 +269,8 @@ void readWizardImageAndDecompressor(std::istream & is, const InnoVersion & versi } if(header.compressMethod == SetupHeader::BZip2 - || (header.compressMethod == SetupHeader::LZMA1 && version == INNO_VERSION(4, 1, 5)) - || (header.compressMethod == SetupHeader::Zlib && version >= INNO_VERSION(4, 2, 6))) { + || (header.compressMethod == SetupHeader::LZMA1 && version == INNO_VERSION(4, 1, 5)) + || (header.compressMethod == SetupHeader::Zlib && version >= INNO_VERSION(4, 2, 6))) { dump(is, "decompressor"); } @@ -290,29 +295,25 @@ int main(int argc, char * argv[]) { return 1; } - uint64_t fileSize = ifs.tellg(); - if(!fileSize) { - LogError << "cannot read file or empty file"; - return 1; - } - SetupLoader offsets; offsets.load(ifs); cout << std::boolalpha; cout << "loaded offsets:" << endl; - cout << "- total size: " << color::cyan << PrintBytes(offsets.totalSize) << color::reset << endl; if(offsets.exeOffset) { cout << "- exe: @ " << color::cyan << PrintHex(offsets.exeOffset) << color::reset; if(offsets.exeCompressedSize) { - cout << " compressed: " << color::cyan << PrintHex(offsets.exeCompressedSize) << color::reset; + cout << " compressed: " << color::cyan << PrintHex(offsets.exeCompressedSize) + << color::reset; } - cout << " uncompressed: " << color::cyan << PrintBytes(offsets.exeUncompressedSize) << color::reset << endl; + cout << " uncompressed: " << color::cyan << PrintBytes(offsets.exeUncompressedSize) + << color::reset << endl; cout << "- exe checksum: " << color::cyan << offsets.exeChecksum << color::reset << endl; } cout << IfNotZero("- message offset", PrintHex(offsets.messageOffset)); - cout << "- header offset: " << color::cyan << PrintHex(offsets.headerOffset) << color::reset << endl; + cout << "- header offset: " << color::cyan << PrintHex(offsets.headerOffset) + << color::reset << endl; cout << IfNotZero("- data offset", PrintHex(offsets.dataOffset)); ifs.seekg(offsets.headerOffset); @@ -419,15 +420,21 @@ int main(int argc, char * argv[]) { cout << IfNotZero("Slices per disk", header.slicesPerDisk); cout << IfNot("Install mode", header.installMode, SetupHeader::NormalInstallMode); - cout << "Uninstall log mode: " << color::cyan << header.uninstallLogMode << color::reset << endl; + cout << "Uninstall log mode: " << color::cyan << header.uninstallLogMode + << color::reset << endl; cout << "Uninstall style: " << color::cyan << header.uninstallStyle << color::reset << endl; - cout << "Dir exists warning: " << color::cyan << header.dirExistsWarning << color::reset << endl; + cout << "Dir exists warning: " << color::cyan << header.dirExistsWarning + << color::reset << endl; cout << IfNot("Privileges required", header.privilegesRequired, SetupHeader::NoPrivileges); - cout << "Show language dialog: " << color::cyan << header.showLanguageDialog << color::reset << endl; - cout << IfNot("Danguage detection", header.languageDetectionMethod, SetupHeader::NoLanguageDetection); + cout << "Show language dialog: " << color::cyan << header.showLanguageDialog + << color::reset << endl; + cout << IfNot("Danguage detection", header.languageDetectionMethod, + SetupHeader::NoLanguageDetection); cout << "Compression: " << color::cyan << header.compressMethod << color::reset << endl; - cout << "Architectures allowed: " << color::cyan << header.architecturesAllowed << color::reset << endl; - cout << "Architectures installed in 64-bit mode: " << color::cyan << header.architecturesInstallIn64BitMode << color::reset << endl; + cout << "Architectures allowed: " << color::cyan << header.architecturesAllowed + << color::reset << endl; + cout << "Architectures installed in 64-bit mode: " << color::cyan + << header.architecturesInstallIn64BitMode << color::reset << endl; if(header.options & SetupHeader::SignedUninstaller) { cout << IfNotZero("Size before signing uninstaller", header.signedUninstallerOrigSize); @@ -435,7 +442,8 @@ int main(int argc, char * argv[]) { } cout << "Disable dir page: " << color::cyan << header.disableDirPage << color::reset << endl; - cout << "Disable program group page: " << color::cyan << header.disableProgramGroupPage << color::reset << endl; + cout << "Disable program group page: " << color::cyan << header.disableProgramGroupPage + << color::reset << endl; cout << IfNotZero("Uninstall display size", header.uninstallDisplaySize); @@ -467,7 +475,8 @@ int main(int argc, char * argv[]) { cout << IfNotEmpty(" Info before text", entry.infoBeforeText); cout << IfNotEmpty(" Info after text", entry.infoAfterText); - cout << " Language id: " << color::cyan << hex << entry.languageId << dec << color::reset << endl; + cout << " Language id: " << color::cyan << hex << entry.languageId << dec + << color::reset << endl; cout << IfNotZero(" Codepage", entry.codepage); cout << IfNotZero(" Dialog font size", entry.dialogFontSize); @@ -498,21 +507,22 @@ int main(int argc, char * argv[]) { LogWarning << "unexpected language index: " << entry.language; } - int codepage; - if(entry.language == -1) { + uint32_t codepage; + if(entry.language < 0) { codepage = version.codepage(); } else { - codepage = languages[entry.language].codepage; + codepage = languages[size_t(entry.language)].codepage; } string decoded; toUtf8(entry.value, decoded, codepage); cout << " - " << Quoted(entry.name); - if(entry.language == -1) { + if(entry.language < 0) { cout << " (default) = "; } else { - cout << " (" << color::cyan << languages[entry.language].name << color::reset << ") = "; + cout << " (" << color::cyan << languages[size_t(entry.language)].name + << color::reset << ") = "; } cout << Quoted(decoded) << endl; @@ -663,7 +673,7 @@ int main(int argc, char * argv[]) { } else { cout << " - " << Quoted(entry.destination); } - if(entry.location != -1) { + if(entry.location != uint32_t(-1)) { cout << " (location: " << color::cyan << entry.location << color::reset << ')'; } cout << endl; @@ -900,11 +910,11 @@ int main(int argc, char * argv[]) { cout << " Timestamp: " << color::cyan << (t.tm_year + 1900) << '-' << std::setfill('0') << std::setw(2) << (t.tm_mon + 1) - << '-' << std::setfill('0') << std::setw(2) << t.tm_mday - << ' ' << std::setfill(' ') << std::setw(2) << t.tm_hour - << ':' << std::setfill('0') << std::setw(2) << t.tm_min - << ':' << std::setfill('0') << std::setw(2) << t.tm_sec - << color::reset << " +" << entry.timestamp.tv_nsec << endl; + << '-' << std::setfill('0') << std::setw(2) << t.tm_mday + << ' ' << std::setfill(' ') << std::setw(2) << t.tm_hour + << ':' << std::setfill('0') << std::setw(2) << t.tm_min + << ':' << std::setfill('0') << std::setw(2) << t.tm_sec + << color::reset << " +" << entry.timestamp.tv_nsec << endl; cout << IfNotZero(" Options", entry.options); @@ -937,8 +947,12 @@ int main(int argc, char * argv[]) { Chunks chunks; for(size_t i = 0; i < locations.size(); i++) { const FileLocationEntry & location = locations[i]; - chunks[ChunkReader::Chunk(location.firstSlice, location.chunkOffset, location.chunkSize, location.options & FileLocationEntry::ChunkCompressed, location.options & FileLocationEntry::ChunkEncrypted)].push_back(i); - assert(header.compressMethod == SetupHeader::BZip2 || !(location.options & FileLocationEntry::BZipped)); + chunks[ChunkReader::Chunk(location.firstSlice, location.chunkOffset, location.chunkSize, + location.options & FileLocationEntry::ChunkCompressed, + location.options & FileLocationEntry::ChunkEncrypted) + ].push_back(i); + assert(header.compressMethod == SetupHeader::BZip2 + || !(location.options & FileLocationEntry::BZipped)); } boost::shared_ptr slice_reader; @@ -948,15 +962,17 @@ int main(int argc, char * argv[]) { } else { fs::path path(argv[1]); - slice_reader.reset(new SliceReader(path.parent_path().string() + '/', path.stem().string(), header.slicesPerDisk)); + slice_reader.reset(new SliceReader(path.parent_path().string() + '/', + path.stem().string(), header.slicesPerDisk)); } try { BOOST_FOREACH(Chunks::value_type & chunk, chunks) { - cout << "[starting " << (chunk.first.compressed ? header.compressMethod : SetupHeader::Stored) << " chunk @ " << chunk.first.firstSlice << " + " << PrintHex(offsets.dataOffset) << " + " - << PrintHex(chunk.first.chunkOffset) << ']' << std::endl; + cout << "[starting " << (chunk.first.compressed ? header.compressMethod : SetupHeader::Stored) + << " chunk @ " << chunk.first.firstSlice << " + " << PrintHex(offsets.dataOffset) + << " + " << PrintHex(chunk.first.chunkOffset) << ']' << std::endl; std::sort(chunk.second.begin(), chunk.second.end(), FileLocationComparer(locations)); @@ -987,7 +1003,8 @@ int main(int argc, char * argv[]) { } } - chunk_source.push(io::restrict(boost::ref(*slice_reader.get()), 0, chunk.first.chunkSize)); + int64_t csize = int64_t(chunk.first.chunkSize); + chunk_source.push(io::restrict(boost::ref(*slice_reader.get()), 0, csize)); uint64_t offset = 0; @@ -1022,7 +1039,8 @@ int main(int argc, char * argv[]) { Hasher hasher; hasher.init(location.checksum.type); - io::restriction raw_src(chunk_source, 0, location.fileSize); + int64_t file_size = int64_t(location.fileSize); + io::restriction raw_src(chunk_source, 0, file_size); io::filtering_istream file_source; @@ -1038,10 +1056,6 @@ int main(int argc, char * argv[]) { file_source.push(raw_src); - //file_source.exceptions(std::ios_base::badbit | std::ios_base::failbit); - - //discard(file, location.fileSize); - BOOST_FOREACH(size_t file_i, files_for_location[location_i]) { if(!files[file_i].destination.empty()) { std::ofstream ofs(files[file_i].destination.c_str()); @@ -1054,7 +1068,7 @@ int main(int argc, char * argv[]) { std::ostringstream oss; float last_rate = 0; - int32_t last_milliseconds = 0; + int64_t last_milliseconds = 0; boost::posix_time::ptime start(boost::posix_time::microsec_clock::universal_time()); @@ -1066,23 +1080,24 @@ int main(int argc, char * argv[]) { ofs.write(buffer, n); - total += n; - float new_status = size_t(1000.f * total / location.fileSize) - * (1 / 1000.f); + total += uint64_t(n); + float new_status = float(size_t(1000.f * float(total) / float(location.fileSize))) + * (1 / 1000.f); if(status != new_status && new_status != 100.f) { boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); - int32_t milliseconds = (now - start).total_milliseconds(); + int64_t milliseconds = (now - start).total_milliseconds(); if(milliseconds - last_milliseconds > 200) { last_milliseconds = milliseconds; if(total >= 10 * 1024 && milliseconds > 0) { - float rate = 1000.f * total / milliseconds; + float rate = 1000.f * float(total) / float(milliseconds); if(rate != last_rate) { last_rate = rate; oss.str(string()); // clear the buffer - oss << std::right << std::fixed << std::setfill(' ') << std::setw(8) << PrintBytes(rate) << "/s"; + oss << std::right << std::fixed << std::setfill(' ') << std::setw(8) + << PrintBytes(rate) << "/s"; } } @@ -1093,7 +1108,6 @@ int main(int argc, char * argv[]) { } } - //io::copy(file_source, ofs, 8192); break; // TODO ... } } diff --git a/src/crypto/ARC4.cpp b/src/crypto/ARC4.cpp deleted file mode 100644 index b5c2730..0000000 --- a/src/crypto/ARC4.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// arc4.cpp - written and placed in the public domain by Wei Dai - -// The ARC4 algorithm was first revealed in an anonymous email to the -// cypherpunks mailing list. This file originally contained some -// code copied from this email. The code has since been rewritten in order -// to clarify the copyright status of this file. It should now be -// completely in the public domain. - -#include "pch.h" -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include "arc4.h" - -NAMESPACE_BEGIN(CryptoPP) -namespace Weak1 { - -void ARC4_TestInstantiations() -{ - ARC4 x; -} - -ARC4_Base::~ARC4_Base() -{ - m_x = m_y = 0; -} - -void ARC4_Base::UncheckedSetKey(const byte *key, unsigned int keyLen, const NameValuePairs ¶ms) -{ - AssertValidKeyLength(keyLen); - - m_x = 1; - m_y = 0; - - unsigned int i; - for (i=0; i<256; i++) - m_state[i] = i; - - unsigned int keyIndex = 0, stateIndex = 0; - for (i=0; i<256; i++) - { - unsigned int a = m_state[i]; - stateIndex += key[keyIndex] + a; - stateIndex &= 0xff; - m_state[i] = m_state[stateIndex]; - m_state[stateIndex] = a; - if (++keyIndex >= keyLen) - keyIndex = 0; - } - - int discardBytes = params.GetIntValueWithDefault("DiscardBytes", GetDefaultDiscardBytes()); - DiscardBytes(discardBytes); -} - -template -static inline unsigned int MakeByte(T &x, T &y, byte *s) -{ - unsigned int a = s[x]; - y = (y+a) & 0xff; - unsigned int b = s[y]; - s[x] = b; - s[y] = a; - x = (x+1) & 0xff; - return s[(a+b) & 0xff]; -} - -void ARC4_Base::GenerateBlock(byte *output, size_t size) -{ - while (size--) - *output++ = MakeByte(m_x, m_y, m_state); -} - -void ARC4_Base::ProcessData(byte *outString, const byte *inString, size_t length) -{ - if (length == 0) - return; - - byte *const s = m_state; - unsigned int x = m_x; - unsigned int y = m_y; - - if (inString == outString) - { - do - { - *outString++ ^= MakeByte(x, y, s); - } while (--length); - } - else - { - do - { - *outString++ = *inString++ ^ MakeByte(x, y, s); - } - while(--length); - } - - m_x = x; - m_y = y; -} - -void ARC4_Base::DiscardBytes(size_t length) -{ - if (length == 0) - return; - - byte *const s = m_state; - unsigned int x = m_x; - unsigned int y = m_y; - - do - { - MakeByte(x, y, s); - } - while(--length); - - m_x = x; - m_y = y; -} - -} -NAMESPACE_END diff --git a/src/crypto/ARC4.h b/src/crypto/ARC4.h deleted file mode 100644 index 9dcc92e..0000000 --- a/src/crypto/ARC4.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef CRYPTOPP_ARC4_H -#define CRYPTOPP_ARC4_H - -#include "strciphr.h" - -NAMESPACE_BEGIN(CryptoPP) - -namespace Weak1 { - -//! _ -class CRYPTOPP_NO_VTABLE ARC4_Base : public VariableKeyLength<16, 1, 256>, public RandomNumberGenerator, public SymmetricCipher, public SymmetricCipherDocumentation -{ -public: - ~ARC4_Base(); - - static const char *StaticAlgorithmName() {return "ARC4";} - - void GenerateBlock(byte *output, size_t size); - void DiscardBytes(size_t n); - - void ProcessData(byte *outString, const byte *inString, size_t length); - - bool IsRandomAccess() const {return false;} - bool IsSelfInverting() const {return true;} - bool IsForwardTransformation() const {return true;} - - typedef SymmetricCipherFinal Encryption; - typedef SymmetricCipherFinal Decryption; - -protected: - void UncheckedSetKey(const byte *key, unsigned int length, const NameValuePairs ¶ms); - virtual unsigned int GetDefaultDiscardBytes() const {return 0;} - - FixedSizeSecBlock m_state; - byte m_x, m_y; -}; - -//! Alleged RC4 -DOCUMENTED_TYPEDEF(SymmetricCipherFinal, ARC4) - -//! _ -class CRYPTOPP_NO_VTABLE MARC4_Base : public ARC4_Base -{ -public: - static const char *StaticAlgorithmName() {return "MARC4";} - - typedef SymmetricCipherFinal Encryption; - typedef SymmetricCipherFinal Decryption; - -protected: - unsigned int GetDefaultDiscardBytes() const {return 256;} -}; - -//! Modified ARC4: it discards the first 256 bytes of keystream which may be weaker than the rest -DOCUMENTED_TYPEDEF(SymmetricCipherFinal, MARC4) - -} -#if CRYPTOPP_ENABLE_NAMESPACE_WEAK >= 1 -namespace Weak {using namespace Weak1;} // import Weak1 into CryptoPP::Weak -#else -using namespace Weak1; // import Weak1 into CryptoPP with warning -#ifdef __GNUC__ -#warning "You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning." -#else -#pragma message("You may be using a weak algorithm that has been retained for backwards compatibility. Please '#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1' before including this .h file and prepend the class name with 'Weak::' to remove this warning.") -#endif -#endif - -NAMESPACE_END - -#endif diff --git a/src/crypto/Adler-32.cpp b/src/crypto/Adler-32.cpp index 5047b9f..e75e2d3 100644 --- a/src/crypto/Adler-32.cpp +++ b/src/crypto/Adler-32.cpp @@ -6,10 +6,10 @@ void Adler32::update(const char * input, size_t length) { - const unsigned long BASE = 65521; + const uint_fast32_t BASE = 65521; - unsigned long s1 = this->s1; - unsigned long s2 = this->s2; + uint_fast32_t s1 = this->s1; + uint_fast32_t s2 = this->s2; if(length % 8 != 0) { @@ -28,14 +28,14 @@ void Adler32::update(const char * input, size_t length) { while(length > 0) { - s1 += uint8_t(input[0]); s2 += s1; - s1 += uint8_t(input[1]); s2 += s1; - s1 += uint8_t(input[2]); s2 += s1; - s1 += uint8_t(input[3]); s2 += s1; - s1 += uint8_t(input[4]); s2 += s1; - s1 += uint8_t(input[5]); s2 += s1; - s1 += uint8_t(input[6]); s2 += s1; - s1 += uint8_t(input[7]); s2 += s1; + s1 += uint8_t(input[0]), s2 += s1; + s1 += uint8_t(input[1]), s2 += s1; + s1 += uint8_t(input[2]), s2 += s1; + s1 += uint8_t(input[3]), s2 += s1; + s1 += uint8_t(input[4]), s2 += s1; + s1 += uint8_t(input[5]), s2 += s1; + s1 += uint8_t(input[6]), s2 += s1; + s1 += uint8_t(input[7]), s2 += s1; length -= 8; input += 8; diff --git a/src/crypto/Adler-32.hpp b/src/crypto/Adler-32.hpp index 8013a77..4733576 100644 --- a/src/crypto/Adler-32.hpp +++ b/src/crypto/Adler-32.hpp @@ -2,13 +2,14 @@ #ifndef INNOEXTRACT_CRYPTO_ADLER32_HPP #define INNOEXTRACT_CRYPTO_ADLER32_HPP +#include #include #include "crypto/Checksum.hpp" -//! ADLER-32 checksum calculations +//! ADLER-32 checksum calculations struct Adler32 : public ChecksumBase { - void init() { s1 = 1; s2 = 0; } + void init() { s1 = 1, s2 = 0; } void update(const char * data, size_t length); diff --git a/src/crypto/IteratedHash.hpp b/src/crypto/IteratedHash.hpp index c09325c..556ad7b 100644 --- a/src/crypto/IteratedHash.hpp +++ b/src/crypto/IteratedHash.hpp @@ -15,13 +15,13 @@ inline bool isPowerOf2(const T & n) { } template -inline T2 modPowerOf2(const T1 &a, const T2 &b) { - return T2(a) & (b-1); +inline T2 modPowerOf2(const T1 & a, const T2 & b) { + return T2(a) & (b - 1); } template inline unsigned int getAlignmentOf() { -#if (_MSC_VER >= 1300) +#if defined(_MSC_VER) && _MSC_VER >= 1300 return __alignof(T); #elif defined(__GNUC__) return __alignof__(T); @@ -31,7 +31,8 @@ inline unsigned int getAlignmentOf() { } inline bool isAlignedOn(const void * p, unsigned int alignment) { - return alignment==1 || (isPowerOf2(alignment) ? modPowerOf2((size_t)p, alignment) == 0 : (size_t)p % alignment == 0); + return alignment == 1 || (isPowerOf2(alignment) ? modPowerOf2(size_t(p), alignment) == 0 + : size_t(p) % alignment == 0); } template @@ -45,12 +46,12 @@ template<> struct SafeShifter { template - static inline T RightShift(T value, unsigned int bits) { + static inline T RightShift(T, unsigned int) { return 0; } template - static inline T LeftShift(T value, unsigned int bits) { + static inline T LeftShift(T, unsigned int) { return 0; } }; @@ -71,12 +72,12 @@ struct SafeShifter { template inline T SafeRightShift(T value) { - return SafeShifter<(bits>=(8*sizeof(T)))>::RightShift(value, bits); + return SafeShifter<(bits >= (8 * sizeof(T)))>::RightShift(value, bits); } template inline T SafeLeftShift(T value) { - return SafeShifter<(bits>=(8*sizeof(T)))>::LeftShift(value, bits); + return SafeShifter<(bits >= (8 * sizeof(T)))>::LeftShift(value, bits); } template @@ -116,7 +117,7 @@ private: template void IteratedHash::update(const char * input, size_t len) { - HashWord oldCountLo = countLo, oldCountHi = countHi; + HashWord oldCountLo = countLo; if((countLo = oldCountLo + HashWord(len)) < oldCountLo) { countHi++; // carry from low to high @@ -124,7 +125,7 @@ void IteratedHash::update(const char * input, size_t len) { countHi += HashWord(SafeRightShift<8 * sizeof(HashWord)>(len)); - unsigned int num = modPowerOf2(oldCountLo, size_t(BlockSize)); + size_t num = modPowerOf2(oldCountLo, size_t(BlockSize)); uint8_t * d = reinterpret_cast(data); if(num != 0) { // process left over data @@ -155,7 +156,7 @@ void IteratedHash::update(const char * input, size_t len) { hash(data, BlockSize); input += BlockSize; len -= BlockSize; - } while (len >= BlockSize); + } while(len >= BlockSize); } } @@ -187,16 +188,16 @@ size_t IteratedHash::hash(const HashWord * input, size_t length) { template void IteratedHash::pad(unsigned int lastBlockSize, uint8_t padFirst) { - unsigned int num = modPowerOf2(countLo, size_t(BlockSize)); + size_t num = modPowerOf2(countLo, size_t(BlockSize)); uint8_t * d = reinterpret_cast(data); d[num++] = padFirst; if(num <= lastBlockSize) { - memset(d + num, 0, lastBlockSize-num); + memset(d + num, 0, lastBlockSize - num); } else { - memset(d+num, 0, BlockSize-num); + memset(d + num, 0, BlockSize - num); hash(data, BlockSize); memset(d, 0, lastBlockSize); } @@ -206,7 +207,7 @@ template inline T rotlFixed(T x, unsigned int y) { return T((x << y) | (x >> (sizeof(T) * 8 - y))); } -#if _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) +#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__INTEL_COMPILER) template<> inline uint8_t rotlFixed(uint8_t x, unsigned int y) { return y ? _rotl8(x, y) : x; @@ -225,8 +226,9 @@ template<> inline uint32_t rotlFixed(uint32_t x, unsigned int y) { } #endif -#if _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) -// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when using these instructions +#if defined(_MSC_VER) && _MSC_VER >= 1300 && !defined(__INTEL_COMPILER) +// Intel C++ Compiler 10.0 calls a function instead of using the rotate instruction when +// using these instructions template<> inline uint64_t rotlFixed(uint64_t x, unsigned int y) { return y ? _rotl64(x, y) : x; } @@ -234,15 +236,15 @@ template<> inline uint64_t rotlFixed(uint64_t x, unsigned int y) { template void IteratedHash::finalize(char * digest) { - - int order = ByteOrder::offset; - + + size_t order = ByteOrder::offset; + pad(BlockSize - 2 * sizeof(HashWord)); data[BlockSize / sizeof(HashWord) - 2 + order] = ByteOrder::byteSwapIfAlien(getBitCountLo()); data[BlockSize / sizeof(HashWord) - 1 - order] = ByteOrder::byteSwapIfAlien(getBitCountHi()); - + hash(data, BlockSize); - + if(isAligned(digest) && HashSize % sizeof(HashWord) == 0) { ByteOrder::byteSwapIfAlien(state, reinterpret_cast(digest), HashSize); } else { diff --git a/src/crypto/SHA-1.cpp b/src/crypto/SHA-1.cpp index 79c9f89..d727bfb 100644 --- a/src/crypto/SHA-1.cpp +++ b/src/crypto/SHA-1.cpp @@ -15,19 +15,25 @@ void Sha1Transform::init(HashWord * state) { void Sha1Transform::transform(HashWord * state, const HashWord * data) { #define blk0(i) (W[i] = data[i]) -#define blk1(i) (W[i&15] = rotlFixed(W[(i+13)&15]^W[(i+8)&15]^W[(i+2)&15]^W[i&15],1)) +#define blk1(i) (W[i & 15] = rotlFixed(W[(i + 13) & 15] ^ W[(i + 8) & 15] \ + ^ W[(i + 2) & 15] ^ W[i & 15], 1)) -#define f1(x,y,z) (z^(x&(y^z))) -#define f2(x,y,z) (x^y^z) -#define f3(x,y,z) ((x&y)|(z&(x|y))) -#define f4(x,y,z) (x^y^z) +#define f1(x, y, z) (z ^ (x & (y ^ z))) +#define f2(x, y, z) (x ^ y ^ z) +#define f3(x, y, z) ((x & y) | (z & (x | y))) +#define f4(x, y, z) (x ^ y ^ z) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=f1(w,x,y)+blk0(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R1(v,w,x,y,z,i) z+=f1(w,x,y)+blk1(i)+0x5A827999+rotlFixed(v,5);w=rotlFixed(w,30); -#define R2(v,w,x,y,z,i) z+=f2(w,x,y)+blk1(i)+0x6ED9EBA1+rotlFixed(v,5);w=rotlFixed(w,30); -#define R3(v,w,x,y,z,i) z+=f3(w,x,y)+blk1(i)+0x8F1BBCDC+rotlFixed(v,5);w=rotlFixed(w,30); -#define R4(v,w,x,y,z,i) z+=f4(w,x,y)+blk1(i)+0xCA62C1D6+rotlFixed(v,5);w=rotlFixed(w,30); +#define R0(v, w, x, y, z, i) z += f1(w, x, y) + blk0(i) + 0x5A827999 + rotlFixed(v, 5); \ + w = rotlFixed(w, 30); +#define R1(v, w, x, y, z, i) z += f1(w, x, y) + blk1(i) + 0x5A827999 + rotlFixed(v, 5); \ + w = rotlFixed(w, 30); +#define R2(v, w, x, y, z, i) z += f2(w, x, y) + blk1(i) + 0x6ED9EBA1 + rotlFixed(v, 5); \ + w = rotlFixed(w, 30); +#define R3(v, w, x, y, z, i) z += f3(w, x, y) + blk1(i) + 0x8F1BBCDC + rotlFixed(v, 5); \ + w = rotlFixed(w, 30); +#define R4(v, w, x, y, z, i) z += f4(w, x, y) + blk1(i) + 0xCA62C1D6 + rotlFixed(v, 5); \ + w = rotlFixed(w, 30); HashWord W[16]; @@ -39,26 +45,106 @@ void Sha1Transform::transform(HashWord * state, const HashWord * data) { HashWord e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + + R0(a, b, c, d, e, 0); + R0(e, a, b, c, d, 1); + R0(d, e, a, b, c, 2); + R0(c, d, e, a, b, 3); + + R0(b, c, d, e, a, 4); + R0(a, b, c, d, e, 5); + R0(e, a, b, c, d, 6); + R0(d, e, a, b, c, 7); + + R0(c, d, e, a, b, 8); + R0(b, c, d, e, a, 9); + R0(a, b, c, d, e, 10); + R0(e, a, b, c, d, 11); + + R0(d, e, a, b, c, 12); + R0(c, d, e, a, b, 13); + R0(b, c, d, e, a, 14); + R0(a, b, c, d, e, 15); + + R1(e, a, b, c, d, 16); + R1(d, e, a, b, c, 17); + R1(c, d, e, a, b, 18); + R1(b, c, d, e, a, 19); + + R2(a, b, c, d, e, 20); + R2(e, a, b, c, d, 21); + R2(d, e, a, b, c, 22); + R2(c, d, e, a, b, 23); + + R2(b, c, d, e, a, 24); + R2(a, b, c, d, e, 25); + R2(e, a, b, c, d, 26); + R2(d, e, a, b, c, 27); + + R2(c, d, e, a, b, 28); + R2(b, c, d, e, a, 29); + R2(a, b, c, d, e, 30); + R2(e, a, b, c, d, 31); + + R2(d, e, a, b, c, 32); + R2(c, d, e, a, b, 33); + R2(b, c, d, e, a, 34); + R2(a, b, c, d, e, 35); + + R2(e, a, b, c, d, 36); + R2(d, e, a, b, c, 37); + R2(c, d, e, a, b, 38); + R2(b, c, d, e, a, 39); + + R3(a, b, c, d, e, 40); + R3(e, a, b, c, d, 41); + R3(d, e, a, b, c, 42); + R3(c, d, e, a, b, 43); + + R3(b, c, d, e, a, 44); + R3(a, b, c, d, e, 45); + R3(e, a, b, c, d, 46); + R3(d, e, a, b, c, 47); + + R3(c, d, e, a, b, 48); + R3(b, c, d, e, a, 49); + R3(a, b, c, d, e, 50); + R3(e, a, b, c, d, 51); + + R3(d, e, a, b, c, 52); + R3(c, d, e, a, b, 53); + R3(b, c, d, e, a, 54); + R3(a, b, c, d, e, 55); + + R3(e, a, b, c, d, 56); + R3(d, e, a, b, c, 57); + R3(c, d, e, a, b, 58); + R3(b, c, d, e, a, 59); + + R4(a, b, c, d, e, 60); + R4(e, a, b, c, d, 61); + R4(d, e, a, b, c, 62); + R4(c, d, e, a, b, 63); + + R4(b, c, d, e, a, 64); + R4(a, b, c, d, e, 65); + R4(e, a, b, c, d, 66); + R4(d, e, a, b, c, 67); + + R4(c, d, e, a, b, 68); + R4(b, c, d, e, a, 69); + R4(a, b, c, d, e, 70); + R4(e, a, b, c, d, 71); + + R4(d, e, a, b, c, 72); + R4(c, d, e, a, b, 73); + R4(b, c, d, e, a, 74); + R4(a, b, c, d, e, 75); + + R4(e, a, b, c, d, 76); + R4(d, e, a, b, c, 77); + R4(c, d, e, a, b, 78); + R4(b, c, d, e, a, 79); /* Add the working vars back into context.state[] */ state[0] += a; diff --git a/src/crypto/SHA-1.hpp b/src/crypto/SHA-1.hpp index 1da8da3..bbce7f5 100644 --- a/src/crypto/SHA-1.hpp +++ b/src/crypto/SHA-1.hpp @@ -19,6 +19,6 @@ public: static void transform(HashWord * digest, const HashWord * data); }; -typedef IteratedHash Sha1; +typedef IteratedHash Sha1; #endif // INNOEXTRACT_CRYPTO_SHA1_HPP diff --git a/src/loader/ExeReader.cpp b/src/loader/ExeReader.cpp index 11a3433..8e6f7c9 100644 --- a/src/loader/ExeReader.cpp +++ b/src/loader/ExeReader.cpp @@ -14,9 +14,9 @@ namespace { static const char PE_MAGIC[] = { 'P', 'E', 0, 0 }; -inline bool getResourceTable(size_t & entry, size_t resource_offset) { +inline bool getResourceTable(uint32_t & entry, uint32_t resource_offset) { - bool is_table = (entry & (1 << 31)); + bool is_table = (entry & (uint32_t(1) << 31)); entry &= ~(1 << 31), entry += resource_offset; @@ -44,7 +44,7 @@ struct ExeReader::CoffSection { }; -size_t ExeReader::findResourceEntry(std::istream & is, int needle) { +uint32_t ExeReader::findResourceEntry(std::istream & is, uint32_t needle) { // skip: characteristics + timestamp + major version + minor version if(is.seekg(4 + 4 + 2 + 2, std::ios_base::cur).fail()) { @@ -59,7 +59,7 @@ size_t ExeReader::findResourceEntry(std::istream & is, int needle) { // Ignore named resource entries. - const size_t entry_size = 4 + 4; // id / string address + offset + const uint32_t entry_size = 4 + 4; // id / string address + offset if(is.seekg(nbnames * entry_size, std::ios_base::cur).fail()) { return 0; } @@ -80,13 +80,13 @@ size_t ExeReader::findResourceEntry(std::istream & is, int needle) { return 0; } -bool ExeReader::loadSectionTable(std::istream & is, size_t peOffset, +bool ExeReader::loadSectionTable(std::istream & is, uint32_t peOffset, const CoffFileHeader & coff, CoffSectionTable & table) { // machine + nsections + creation time + symbol table offset + nsymbols // + optional header size + characteristics - const size_t file_header_size = 2 + 2 + 4 + 4 + 4 + 2 + 2; - size_t section_table_offset = peOffset + sizeof(PE_MAGIC) + file_header_size + const uint32_t file_header_size = 2 + 2 + 4 + 4 + 4 + 2 + 2; + uint32_t section_table_offset = peOffset + uint32_t(sizeof(PE_MAGIC)) + file_header_size + coff.optional_header_size; is.seekg(section_table_offset); @@ -110,7 +110,7 @@ bool ExeReader::loadSectionTable(std::istream & is, size_t peOffset, return !is.fail(); } -size_t ExeReader::memoryAddressToFileOffset(const CoffSectionTable & sections, size_t memory) { +uint32_t ExeReader::memoryAddressToFileOffset(const CoffSectionTable & sections, uint32_t memory) { for(CoffSectionTable::const_iterator i = sections.begin(); i != sections.end(); ++i) { const CoffSection & section = *i; @@ -125,7 +125,8 @@ size_t ExeReader::memoryAddressToFileOffset(const CoffSectionTable & sections, s return 0; } -ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int type, int language) { +ExeReader::Resource ExeReader::findResource(std::istream & is, uint32_t name, + uint32_t type, uint32_t language) { Resource result; result.offset = result.size = 0; @@ -166,7 +167,7 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ if(is.fail() || ndirectories < 3) { return result; } - const size_t directory_header_size = 4 + 4; // address + size + const uint32_t directory_header_size = 4 + 4; // address + size is.seekg(2 * directory_header_size, std::ios_base::cur); // Virtual memory address and size of the start of resource directory. @@ -181,25 +182,25 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ return result; } - size_t resource_offset = memoryAddressToFileOffset(sections, resource_address); + uint32_t resource_offset = memoryAddressToFileOffset(sections, resource_address); if(!resource_offset) { return result; } is.seekg(resource_offset); - size_t type_offset = findResourceEntry(is, type); + uint32_t type_offset = findResourceEntry(is, type); if(!getResourceTable(type_offset, resource_offset)) { return result; } is.seekg(type_offset); - size_t name_offset = findResourceEntry(is, name); + uint32_t name_offset = findResourceEntry(is, name); if(!getResourceTable(name_offset, resource_offset)) { return result; } is.seekg(name_offset); - size_t leaf_offset = findResourceEntry(is, language); + uint32_t leaf_offset = findResourceEntry(is, language); if(!leaf_offset || getResourceTable(leaf_offset, resource_offset)) { return result; } @@ -213,7 +214,7 @@ ExeReader::Resource ExeReader::findResource(std::istream & is, int name, int typ return result; } - size_t data_offset = memoryAddressToFileOffset(sections, data_address); + uint32_t data_offset = memoryAddressToFileOffset(sections, data_address); if(!data_offset) { return result; } diff --git a/src/loader/ExeReader.hpp b/src/loader/ExeReader.hpp index cbab8a4..b470630 100644 --- a/src/loader/ExeReader.hpp +++ b/src/loader/ExeReader.hpp @@ -2,7 +2,7 @@ #ifndef INNOEXTRACT_LOADER_EXEREADER_HPP #define INNOEXTRACT_LOADER_EXEREADER_HPP -#include +#include #include #include @@ -16,9 +16,9 @@ public: struct Resource { - size_t offset; + uint32_t offset; - size_t size; + uint32_t size; }; @@ -31,7 +31,8 @@ public: * Find where a resource with a given ID is stored in a MS PE/COFF executable. * @return The location of the resource or (0, 0) if the requested resource does not exist. */ - static Resource findResource(std::istream & is, int name, int type = TypeData, int language = LanguageDefault); + static Resource findResource(std::istream & is, uint32_t name, uint32_t type = TypeData, + uint32_t language = LanguageDefault); private: @@ -51,11 +52,12 @@ private: * Remaining 31 bits: Offset to the CoffResourceTable CoffResourceLeaf relative to * the directory start. */ - static size_t findResourceEntry(std::istream & ifs, int id); + static uint32_t findResourceEntry(std::istream & ifs, uint32_t id); - static bool loadSectionTable(std::istream & ifs, size_t peOffset, const CoffFileHeader & coff, CoffSectionTable & table); + static bool loadSectionTable(std::istream & ifs, uint32_t peOffset, + const CoffFileHeader & coff, CoffSectionTable & table); - static size_t memoryAddressToFileOffset(const CoffSectionTable & sections, size_t memory); + static uint32_t memoryAddressToFileOffset(const CoffSectionTable & sections, uint32_t memory); }; diff --git a/src/loader/SetupLoader.cpp b/src/loader/SetupLoader.cpp index d4654ed..49e031c 100644 --- a/src/loader/SetupLoader.cpp +++ b/src/loader/SetupLoader.cpp @@ -16,7 +16,7 @@ namespace { struct SetupLoaderVersion { - char magic[12]; + unsigned char magic[12]; // Earliest known version with that ID. InnoVersionConstant version; @@ -70,7 +70,7 @@ bool SetupLoader::loadFromExeResource(std::istream & is) { return loadOffsetsAt(is, resource.offset); } -bool SetupLoader::loadOffsetsAt(std::istream & is, size_t pos) { +bool SetupLoader::loadOffsetsAt(std::istream & is, uint32_t pos) { if(is.seekg(pos).fail()) { is.clear(); @@ -107,7 +107,7 @@ bool SetupLoader::loadOffsetsAt(std::istream & is, size_t pos) { } } - totalSize = checksum.load(is); + (void)checksum.load(is); exeOffset = checksum.load(is); if(version >= INNO_VERSION(4, 1, 6)) { @@ -178,8 +178,6 @@ void SetupLoader::load(std::istream & is) { * In that case, the setup headers start at the beginning of the file. */ - totalSize = is.seekg(0, std::ios_base::end).tellg(); - exeCompressedSize = exeUncompressedSize = exeOffset = 0; // No embedded setup exe. messageOffset = 0; // No embedded messages. diff --git a/src/loader/SetupLoader.hpp b/src/loader/SetupLoader.hpp index 07d4447..4928ed5 100644 --- a/src/loader/SetupLoader.hpp +++ b/src/loader/SetupLoader.hpp @@ -9,28 +9,26 @@ struct SetupLoader { - size_t totalSize; //!< Minimum expected size of the setup file - - size_t exeOffset; //!< Offset of compressed setup.e32. 0 means there is no exe in this file. - size_t exeCompressedSize; //!< Size of setup.e32 after compression (0 = unknown) - size_t exeUncompressedSize; //!< Size of setup.e32 before compression + uint32_t exeOffset; //!< Offset of compressed setup.e32. 0 means there is no exe in this file. + uint32_t exeCompressedSize; //!< Size of setup.e32 after compression (0 = unknown) + uint32_t exeUncompressedSize; //!< Size of setup.e32 before compression Checksum exeChecksum; //!< Checksum of setup.e32 before compression - size_t messageOffset; + uint32_t messageOffset; /*! * Offset of embedded setup-0.bin data (the setup headers) * This points to a version string (see setup/Version.hpp) followed by a * compressed block of headers (see stream/BlockReader.hpp and setup/SetupHeader.hpp) */ - size_t headerOffset; + uint32_t headerOffset; /*! * Offset of embedded setup-1.bin data. * If this is zero, the setup data is stored in seprarate files. */ - size_t dataOffset; + uint32_t dataOffset; /*! * Try to find the setup loader offsets in the given file. @@ -43,7 +41,7 @@ private: bool loadFromExeResource(std::istream & is); - bool loadOffsetsAt(std::istream & is, size_t pos); + bool loadOffsetsAt(std::istream & is, uint32_t pos); }; diff --git a/src/misc/BitfieldConverter.hpp b/src/misc/BitfieldConverter.hpp deleted file mode 100644 index ba9ae69..0000000 --- a/src/misc/BitfieldConverter.hpp +++ /dev/null @@ -1,240 +0,0 @@ - -#include -#include -#include -#include -#include - -namespace boost { - template - class integer_traits : public integer_traits { }; -} - -template -struct is_power_of_two { - static const bool value = false; -}; -template -struct is_power_of_two::type> { - static const bool value = true; - typedef Type type; -}; - -template -struct log_next_power_of_two { - static const size_t value = boost::static_log2::value + 1; -}; -template -struct log_next_power_of_two >::type> { - static const size_t value = boost::static_log2::value; -}; - -template -struct next_power_of_two { - static const size_t value = size_t(1) << (boost::static_log2::value + 1); -}; -template -struct next_power_of_two >::type> { - static const size_t value = N; -}; - - -struct fast_integers { - -private: - - template struct _impl { }; - template struct _impl<8, Dummy> { typedef uint_fast8_t type; }; - template struct _impl<16, Dummy> { typedef uint_fast16_t type; }; - template struct _impl<32, Dummy> { typedef uint_fast32_t type; }; - template struct _impl<64, Dummy> { typedef uint_fast64_t type; }; - template struct _impl<128, Dummy> { typedef __uint128_t type; }; - -public: - - template - struct bits : public _impl::value>::value> { }; - -}; - -struct exact_integers { - -private: - - template struct _impl { }; - template struct _impl<8, Dummy> { typedef uint8_t type; }; - template struct _impl<16, Dummy> { typedef uint16_t type; }; - template struct _impl<32, Dummy> { typedef uint32_t type; }; - template struct _impl<64, Dummy> { typedef uint64_t type; }; - template struct _impl<128, Dummy> { typedef __uint128_t type; }; - -public: - - template - struct bits : public _impl::value>::value> { }; - -}; - -struct bitset_types { - - template - struct bits { - typedef std::bitset type; - }; - -}; - - -/*! - * Converter that rearranges bits in an integer. - * - * Conversion is reduced to a minimal number of mask & shift operations at compile-time. - * - * Usage: - * - * bitset_converter<> is an empty converter list (cannot be used without adding at least one mappings). - * (list)::add::map<from, to> maps the from'th input bit to the to'th output bit. - * - * Convenience function to add a continous region of mappings: - * bitset_converter<>::add::value<to2> is equivalent to bitset_converter<>::add::map<0, to2> - * (list)::add::map<from, to>::add::value<to2> is equivalent to ::add::map<from, to>::add::map<from + 1, to2> - * - * Inut bits without a corresponding "from" entry are ignored. - * Output bit without a corresponding "to" entry are always zero. - * - * The same input/output bit can appear in multiple mappings. - * - * Invoke the converter: (list)::convert(integer) - * - * Limitations: - * - * Input bits must fit in a native integer type provided by in_types::bits<bits>. - * - * Output bits must fit in an integer type selected by out_types::bits<bits>. - * - * Example: - * - * // Create a converter that swaps the first two bits, keeps the next one and ignores all others. - * typedef bitset_converter<>::add::map<0, 1>::add::map<1, 0>::add::value<2> Converter; - * - * // Convert something. - * Converter::convert(3); - * - */ -template -struct bitset_converter { - -private: - - typedef ptrdiff_t shift_type; - typedef size_t index_type; - - template - struct IterateEntries { - static const typename Combiner::type value = Combiner::template combine::value)>::value; - }; - template struct IterateEntries { static const typename Combiner::type value = Combiner::base; }; - template struct Combiner { typedef Type type; static const Type base = Base; }; - - template - struct MaxCombiner : public Combiner::const_min> { - template - struct combine { static const Type value = boost::static_signed_max::value, accumulator>::value; }; - }; - - template - struct MinCombiner : public Combiner::const_max> { - template - struct combine { static const Type value = boost::static_signed_min::value, accumulator>::value; }; - }; - - struct ShiftGetter { template struct get { static const shift_type value = Entry::shift; }; }; - struct FromGetter { template struct get { static const index_type value = Entry::from; }; }; - struct ToGetter { template struct get { static const index_type value = Entry::to; }; }; - - template - struct ShiftMaskCombiner : public Combiner { - template - struct combine { static const Type value = mask | ( (Entry::shift == Shift) ? (Type(1) << Entry::from) : Type(0) ); }; - }; - - template - struct Builder; - - template - struct Entry { - - typedef Entry This; - - static const index_type from = From; - static const index_type to = To; - typedef Next next; - - static const shift_type shift = shift_type(from) - shift_type(to); - - static const shift_type max_shift = IterateEntries, This>::value; - static const shift_type min_shift = IterateEntries, This>::value; - - static const index_type in_bits = IterateEntries, This>::value + 1; - typedef typename in_types::template bits::type in_type; - - static const index_type out_bits = IterateEntries, This>::value + 1; - typedef typename out_types::template bits::type out_type; - - template - struct ShiftMask { static const in_type value = IterateEntries, This>::value; }; - - template - inline static typename boost::enable_if_c<(Shift >= shift_type(0)), out_type>::type evaluate(in_type value) { - return out_type((value & ShiftMask::value) >> Shift); - } - template - inline static typename boost::enable_if_c<(Shift < shift_type(0)), out_type>::type evaluate(in_type value) { - return out_type(value & ShiftMask::value) << (-Shift); - } - - template - struct NextShift { static const shift_type value = Shift + 1; }; - template - struct NextShift::value == in_type(0)>::type > { - static const shift_type value = NextShift::value; - }; - - template - inline static typename boost::enable_if_c<(NextShift::value != max_shift + 1), out_type>::type map(in_type value) { - return evaluate(value) | (map::value>(value)); - } - template - inline static typename boost::enable_if_c<(NextShift::value == max_shift + 1), out_type>::type map(in_type value) { - return evaluate(value); - } - - public: - - typedef Builder add; - - static out_type convert(in_type value) { - return map(value); - } - - }; - - template - struct Builder { - - template - struct map : public Entry { }; - - template - struct value : public Entry { }; - - template - struct value : public Entry<0, To> { }; - - }; - -public: - - typedef Builder add; - -}; diff --git a/src/misc/test.cpp b/src/misc/test.cpp deleted file mode 100644 index 876a753..0000000 --- a/src/misc/test.cpp +++ /dev/null @@ -1,189 +0,0 @@ - -#include "src/SetupHeaderFormat.hpp" - -#include -#include - -using std::cout; -using std::endl; -using std::setfill; -using std::setw; -using std::hex; -using std::dec; - -/* - -template -void testlog() { - cout << " lnpot: " << setfill(' ') << setw(3) << log_next_power_of_two::value; -} - -template <> -void testlog<0>() { - cout << " lnpot: " << setfill(' ') << setw(3) << 0; -} - -template -void test() { - - cout << setfill(' ') << setw(5) << N; - cout << ' ' << hex << setfill('0') << setw(4) << N << dec; - cout << ": pot=" << is_power_of_two::value; - cout << " npot: " << setfill(' ') << setw(5) << next_power_of_two::value; - testlog(); - cout << " ebits: " << setfill(' ') << setw(3) << StoredEnumType::bits; - cout << " rebits: " << setfill(' ') << setw(3) << (sizeof(typename StoredEnumType::type) * 8); - cout << " fbits: " << setfill(' ') << setw(5) << StoredFlagType::bits; - cout << " rfbits: " << setfill(' ') << setw(5) << (sizeof(typename StoredFlagType::type) * 8); - - cout << endl; -} - -#define TEST0(D) test<0##D>(); - -#define TEST1(D) \ - TEST0(D##0) \ - TEST0(D##1) \ - TEST0(D##2) \ - TEST0(D##3) \ - TEST0(D##4) \ - TEST0(D##5) \ - TEST0(D##6) \ - TEST0(D##7) \ - -#define TEST2(D) \ - TEST1(D##0) \ - TEST1(D##1) \ - TEST1(D##2) \ - TEST1(D##3) \ - TEST1(D##4) \ - TEST1(D##5) \ - TEST1(D##6) \ - TEST1(D##7) \ - -#define TEST3(D) \ - TEST2(D##0) \ - TEST2(D##1) \ - TEST2(D##2) \ - TEST2(D##3) \ - TEST2(D##4) \ - TEST2(D##5) \ - TEST2(D##6) \ - TEST2(D##7) \ - -enum TestEnum { - - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - - TestEnum__End -}; -ENUM_SIZE_AUTO(TestEnum); - -STORED_ENUM_MAP(TestEnumMap, A, - A, // 0 - B, // 1 - C, // 2 - D, // 2 - E, // 2 - F, // 2 - G, // 2 - H, // 2 - I, // 2 - J, // 2 - K, // 2 - L, // 2 - M, // 2 - N, // 2 - O, // 2 - P, // 2 - Q, // 2 - R, // 2 - S, // 2 - T, // 2 - U, // 2 - V, // 2 - W, // 2 - X, // 2 - Y, // 2 - Z, // 2 -); -*/ - -volatile size_t i; - -typedef BitsetConverter - ::add::map<0, 0> - ::add::map<1, 1> - ::add::map<2, 2> - ::add::map<3, 3> - ::add::map<4, 4> - ::add::map<5, 5> - ::add::map<6, 6> - ::add::map<7, 7> - ::add::map<8, 8> - ::add::map<9, 9> - ::add::map<10, 10> - ::add::map<11, 11> - ::add::map<12, 12> - ::add::map<13, 13> - ::add::map<14, 14> - ::add::map<15, 15> - ::add::map<16, 16> - ::add::map<17, 17> - ::add::map<18, 18> - ::add::map<19, 19> - ::add::map<20, 20> - IdentityConverter; - -static void test2(size_t i) { - - size_t out = IdentityConverter::convert(i); - - cout << i << " = " << std::bitset<64>(i) << " -> " << out << " = " << std::bitset<64>(out) << endl; - -} - -int main() { - - //TEST3() - - /* - test2(0); - test2(1); - test2(2); - test2(3); - test2(4); - test2(5); - test2(6); - test2(7); - test2(8);*/ - - - return IdentityConverter::convert(i); -} - diff --git a/src/setup/FileEntry.cpp b/src/setup/FileEntry.cpp index 5af20fc..50b59b4 100644 --- a/src/setup/FileEntry.cpp +++ b/src/setup/FileEntry.cpp @@ -64,9 +64,10 @@ void FileEntry::load(std::istream & is, const InnoVersion & version) { loadVersionData(is, version); - location = loadNumber(is, version.bits); + location = loadNumber(is, version.bits); attributes = loadNumber(is, version.bits); - externalSize = (version >= INNO_VERSION(4, 0, 0)) ? loadNumber(is) : loadNumber(is); + externalSize = (version >= INNO_VERSION(4, 0, 0)) ? loadNumber(is) + : loadNumber(is); if(version < INNO_VERSION(3, 0, 5)) { FileCopyMode copyMode = StoredEnum(is).get(); diff --git a/src/setup/FileEntry.hpp b/src/setup/FileEntry.hpp index 2c5d444..23edb41 100644 --- a/src/setup/FileEntry.hpp +++ b/src/setup/FileEntry.hpp @@ -63,7 +63,7 @@ struct FileEntry : public SetupItem { std::string installFontName; std::string strongAssemblyName; - int location; //!< index into the file location entry list + uint32_t location; //!< index into the file location entry list uint32_t attributes; uint64_t externalSize; diff --git a/src/setup/FileLocationEntry.cpp b/src/setup/FileLocationEntry.cpp index ac22662..4e99d1c 100644 --- a/src/setup/FileLocationEntry.cpp +++ b/src/setup/FileLocationEntry.cpp @@ -44,7 +44,7 @@ void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { if(version.bits == 16) { - uint32_t date = loadNumber(is); // milliseconds? + int32_t date = loadNumber(is); // milliseconds? // TODO this seems to be off by a few years: // expected ~ 2000-04-18, got 1991-07-28 @@ -63,7 +63,7 @@ void FileLocationEntry::load(std::istream & is, const InnoVersion & version) { filetime -= FILETIME_OFFSET; timestamp.tv_sec = std::time_t(filetime / 10000000); - timestamp.tv_nsec = long(filetime % 10000000) * 100; + timestamp.tv_nsec = int32_t(filetime % 10000000) * 100; } fileVersionMS = loadNumber(is); diff --git a/src/setup/FileLocationEntry.hpp b/src/setup/FileLocationEntry.hpp index 78dfd49..516332a 100644 --- a/src/setup/FileLocationEntry.hpp +++ b/src/setup/FileLocationEntry.hpp @@ -33,7 +33,7 @@ struct FileLocationEntry : public SetupItem { size_t firstSlice; size_t lastSlice; - size_t chunkOffset; //!< offset of the compressed chunk in firstSlice + uint32_t chunkOffset; //!< offset of the compressed chunk in firstSlice uint64_t chunkSize; //! total compressed size of the chunk uint64_t fileOffset; //!< offset of this file within the decompressed chunk diff --git a/src/setup/SetupComponentEntry.cpp b/src/setup/SetupComponentEntry.cpp index 11388c0..66cae4b 100644 --- a/src/setup/SetupComponentEntry.cpp +++ b/src/setup/SetupComponentEntry.cpp @@ -7,26 +7,26 @@ namespace { STORED_FLAGS_MAP(StoredSetupComponentOptions0, - SetupComponentEntry::Fixed, - SetupComponentEntry::Restart, - SetupComponentEntry::DisableNoUninstallWarning, + SetupComponentEntry::Fixed, + SetupComponentEntry::Restart, + SetupComponentEntry::DisableNoUninstallWarning, ); // starting with version 3.0.8 STORED_FLAGS_MAP(StoredSetupComponentOptions1, - SetupComponentEntry::Fixed, - SetupComponentEntry::Restart, - SetupComponentEntry::DisableNoUninstallWarning, - SetupComponentEntry::Exclusive, + SetupComponentEntry::Fixed, + SetupComponentEntry::Restart, + SetupComponentEntry::DisableNoUninstallWarning, + SetupComponentEntry::Exclusive, ); // starting with version 4.2.3 STORED_FLAGS_MAP(StoredSetupComponentOptions2, - SetupComponentEntry::Fixed, - SetupComponentEntry::Restart, - SetupComponentEntry::DisableNoUninstallWarning, - SetupComponentEntry::Exclusive, - SetupComponentEntry::DontInheritCheck, + SetupComponentEntry::Fixed, + SetupComponentEntry::Restart, + SetupComponentEntry::DisableNoUninstallWarning, + SetupComponentEntry::Exclusive, + SetupComponentEntry::DontInheritCheck, ); } // anonymous namespace diff --git a/src/setup/SetupHeader.cpp b/src/setup/SetupHeader.cpp index d160c3a..b504588 100644 --- a/src/setup/SetupHeader.cpp +++ b/src/setup/SetupHeader.cpp @@ -157,13 +157,13 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { numRunEntries = loadNumber(is, version.bits); numUninstallRunEntries = loadNumber(is, version.bits); - size_t licenseSize; - size_t infoBeforeSize; - size_t infoAfterSize; + int32_t licenseSize; + int32_t infoBeforeSize; + int32_t infoAfterSize; if(version < INNO_VERSION(1, 3, 21)) { - licenseSize = loadNumber(is, version.bits); - infoBeforeSize = loadNumber(is, version.bits); - infoAfterSize = loadNumber(is, version.bits); + licenseSize = loadNumber(is, version.bits); + infoBeforeSize = loadNumber(is, version.bits); + infoAfterSize = loadNumber(is, version.bits); } minVersion.load(is, version); @@ -183,7 +183,7 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { } if(version < INNO_VERSION(4, 2, 0)) { - password.crc32 = loadNumber(is), password.type = Checksum::Crc32; + password.crc32 = loadNumber(is), password.type = Checksum::Crc32; } else if(version < INNO_VERSION(5, 3, 9)) { is.read(password.md5, sizeof(password.md5)), password.type = Checksum::MD5; } else { @@ -200,7 +200,7 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { slicesPerDisk = 1; } else { extraDiskSpaceRequired = loadNumber(is); - slicesPerDisk = loadNumber(is); + slicesPerDisk = loadNumber(is); } if(version >= INNO_VERSION(2, 0, 0) && version < INNO_VERSION(5, 0, 0)) { @@ -266,7 +266,7 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { } if(version >= INNO_VERSION(5, 2, 1) && version < INNO_VERSION(5, 3, 10)) { - signedUninstallerOrigSize = loadNumber(is); + signedUninstallerOrigSize = loadNumber(is); signedUninstallerHdrChecksum = loadNumber(is); } else { signedUninstallerOrigSize = signedUninstallerHdrChecksum = 0; @@ -455,21 +455,21 @@ void SetupHeader::load(std::istream & is, const InnoVersion & version) { } if(version < INNO_VERSION(1, 3, 21)) { - if(licenseSize) { + if(licenseSize > 0) { std::string temp; - temp.resize(licenseSize); + temp.resize(size_t(licenseSize)); is.read(&temp[0], licenseSize); toUtf8(temp, licenseText); } - if(infoBeforeSize) { + if(infoBeforeSize > 0) { std::string temp; - temp.resize(infoBeforeSize); + temp.resize(size_t(infoBeforeSize)); is.read(&temp[0], infoBeforeSize); toUtf8(temp, infoBeforeText); } - if(infoAfterSize) { + if(infoAfterSize > 0) { std::string temp; - temp.resize(infoAfterSize); + temp.resize(size_t(infoAfterSize)); is.read(&temp[0], infoAfterSize); toUtf8(temp, infoAfterText); } @@ -536,7 +536,8 @@ ENUM_NAMES(SetupHeader::Options, "Setup Option", "back solid", "overwrite uninst reg entries", ) -BOOST_STATIC_ASSERT(EnumSize::value == EnumNames::count); +BOOST_STATIC_ASSERT(EnumSize::value + == EnumNames::count); ENUM_NAMES(SetupHeader::Architectures, "Architecture", "unknown", diff --git a/src/setup/SetupHeader.hpp b/src/setup/SetupHeader.hpp index 134a026..9171bdd 100644 --- a/src/setup/SetupHeader.hpp +++ b/src/setup/SetupHeader.hpp @@ -152,7 +152,7 @@ struct SetupHeader { Color wizardSmallImageBackColor; Checksum password; - SetupSalt passwordSalt; + SetupSalt passwordSalt; int64_t extraDiskSpaceRequired; size_t slicesPerDisk; @@ -215,7 +215,7 @@ struct SetupHeader { Architectures architecturesAllowed; Architectures architecturesInstallIn64BitMode; - uint64_t signedUninstallerOrigSize; + uint32_t signedUninstallerOrigSize; uint32_t signedUninstallerHdrChecksum; AutoBoolean disableDirPage; diff --git a/src/setup/SetupTypeEntry.cpp b/src/setup/SetupTypeEntry.cpp index 075e1ab..4b87df4 100644 --- a/src/setup/SetupTypeEntry.cpp +++ b/src/setup/SetupTypeEntry.cpp @@ -57,4 +57,4 @@ ENUM_NAMES(SetupTypeEntry::Type, "Setyp Type", "default full", "default compact", "default custom", -) \ No newline at end of file +) diff --git a/src/setup/Version.cpp b/src/setup/Version.cpp index ddd944a..12660de 100644 --- a/src/setup/Version.cpp +++ b/src/setup/Version.cpp @@ -36,74 +36,74 @@ struct KnownSetupDataVersion { }; const KnownSetupDataVersion knownSetupDataVersions[] = { - { "Inno Setup Setup Data (1.3.21)", INNO_VERSION_EXT(1, 3, 21, 0) }, - { "Inno Setup Setup Data (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0) }, - { "Inno Setup Setup Data (2.0.0)", INNO_VERSION_EXT(2, 0, 0, 0) }, - { "Inno Setup Setup Data (2.0.1)", INNO_VERSION_EXT(2, 0, 1, 0) }, // or 2.0.2! - { "Inno Setup Setup Data (2.0.5)", INNO_VERSION_EXT(2, 0, 5, 0) }, - { "Inno Setup Setup Data (2.0.6a)", INNO_VERSION_EXT(2, 0, 6, 0) }, - { "Inno Setup Setup Data (2.0.7)", INNO_VERSION_EXT(2, 0, 7, 0) }, - { "Inno Setup Setup Data (2.0.8)", INNO_VERSION_EXT(2, 0, 8, 0) }, - { "Inno Setup Setup Data (2.0.11)", INNO_VERSION_EXT(2, 0, 11, 0) }, - { "Inno Setup Setup Data (2.0.17)", INNO_VERSION_EXT(2, 0, 17, 0) }, - { "Inno Setup Setup Data (2.0.18)", INNO_VERSION_EXT(2, 0, 18, 0) }, - { "Inno Setup Setup Data (3.0.0a)", INNO_VERSION_EXT(3, 0, 0, 0) }, - { "Inno Setup Setup Data (3.0.1)", INNO_VERSION_EXT(3, 0, 1, 0) }, - { "Inno Setup Setup Data (3.0.3)", INNO_VERSION_EXT(3, 0, 3, 0) }, // or 3.0.4! - { "Inno Setup Setup Data (3.0.5)", INNO_VERSION_EXT(3, 0, 5, 0) }, - { "My Inno Setup Extensions Setup Data (3.0.6.1)", INNO_VERSION_EXT(3, 0, 6, 1) }, - { "Inno Setup Setup Data (4.0.0a)", INNO_VERSION_EXT(4, 0, 0, 0) }, - { "Inno Setup Setup Data (4.0.1)", INNO_VERSION_EXT(4, 0, 1, 0) }, - { "Inno Setup Setup Data (4.0.3)", INNO_VERSION_EXT(4, 0, 3, 0) }, - { "Inno Setup Setup Data (4.0.5)", INNO_VERSION_EXT(4, 0, 5, 0) }, - { "Inno Setup Setup Data (4.0.9)", INNO_VERSION_EXT(4, 0, 9, 0) }, - { "Inno Setup Setup Data (4.0.10)", INNO_VERSION_EXT(4, 0, 10, 0) }, - { "Inno Setup Setup Data (4.0.11)", INNO_VERSION_EXT(4, 0, 11, 0) }, - { "Inno Setup Setup Data (4.1.0)", INNO_VERSION_EXT(4, 1, 0, 0) }, - { "Inno Setup Setup Data (4.1.2)", INNO_VERSION_EXT(4, 1, 2, 0) }, - { "Inno Setup Setup Data (4.1.3)", INNO_VERSION_EXT(4, 1, 3, 0) }, - { "Inno Setup Setup Data (4.1.4)", INNO_VERSION_EXT(4, 1, 4, 0) }, - { "Inno Setup Setup Data (4.1.5)", INNO_VERSION_EXT(4, 1, 5, 0) }, - { "Inno Setup Setup Data (4.1.6)", INNO_VERSION_EXT(4, 1, 6, 0) }, - { "Inno Setup Setup Data (4.1.8)", INNO_VERSION_EXT(4, 1, 8, 0) }, - { "Inno Setup Setup Data (4.2.0)", INNO_VERSION_EXT(4, 2, 0, 0) }, - { "Inno Setup Setup Data (4.2.1)", INNO_VERSION_EXT(4, 2, 1, 0) }, - { "Inno Setup Setup Data (4.2.2)", INNO_VERSION_EXT(4, 2, 2, 0) }, - { "Inno Setup Setup Data (4.2.3)", INNO_VERSION_EXT(4, 2, 3, 0) }, // or 4.2.4! - { "Inno Setup Setup Data (4.2.5)", INNO_VERSION_EXT(4, 2, 5, 0) }, - { "Inno Setup Setup Data (4.2.6)", INNO_VERSION_EXT(4, 2, 6, 0) }, - { "Inno Setup Setup Data (5.0.0)", INNO_VERSION_EXT(5, 0, 0, 0) }, - { "Inno Setup Setup Data (5.0.1)", INNO_VERSION_EXT(5, 0, 1, 0) }, - { "Inno Setup Setup Data (5.0.3)", INNO_VERSION_EXT(5, 0, 3, 0) }, - { "Inno Setup Setup Data (5.0.4)", INNO_VERSION_EXT(5, 0, 4, 0) }, - { "Inno Setup Setup Data (5.1.0)", INNO_VERSION_EXT(5, 1, 0, 0) }, - { "Inno Setup Setup Data (5.1.2)", INNO_VERSION_EXT(5, 1, 2, 0) }, - { "Inno Setup Setup Data (5.1.7)", INNO_VERSION_EXT(5, 1, 7, 0) }, - { "Inno Setup Setup Data (5.1.10)", INNO_VERSION_EXT(5, 1, 10, 0) }, - { "Inno Setup Setup Data (5.1.13)", INNO_VERSION_EXT(5, 1, 13, 0) }, - { "Inno Setup Setup Data (5.2.0)", INNO_VERSION_EXT(5, 2, 0, 0) }, - { "Inno Setup Setup Data (5.2.1)", INNO_VERSION_EXT(5, 2, 1, 0) }, - { "Inno Setup Setup Data (5.2.3)", INNO_VERSION_EXT(5, 2, 3, 0) }, - { "Inno Setup Setup Data (5.2.5)", INNO_VERSION_EXT(5, 2, 5, 0) }, - { "Inno Setup Setup Data (5.2.5) (u)", INNO_VERSION_EXT(5, 2, 5, 0), true }, - { "Inno Setup Setup Data (5.3.0)", INNO_VERSION_EXT(5, 3, 0, 0) }, - { "Inno Setup Setup Data (5.3.0) (u)", INNO_VERSION_EXT(5, 3, 0, 0), true }, - { "Inno Setup Setup Data (5.3.3)", INNO_VERSION_EXT(5, 3, 3, 0) }, - { "Inno Setup Setup Data (5.3.3) (u)", INNO_VERSION_EXT(5, 3, 3, 0), true }, - { "Inno Setup Setup Data (5.3.5)", INNO_VERSION_EXT(5, 3, 5, 0) }, - { "Inno Setup Setup Data (5.3.5) (u)", INNO_VERSION_EXT(5, 3, 5, 0), true }, - { "Inno Setup Setup Data (5.3.6)", INNO_VERSION_EXT(5, 3, 6, 0) }, - { "Inno Setup Setup Data (5.3.6) (u)", INNO_VERSION_EXT(5, 3, 6, 0), true }, - { "Inno Setup Setup Data (5.3.7)", INNO_VERSION_EXT(5, 3, 7, 0) }, - { "Inno Setup Setup Data (5.3.7) (u)", INNO_VERSION_EXT(5, 3, 7, 0), true }, - { "Inno Setup Setup Data (5.3.8)", INNO_VERSION_EXT(5, 3, 8, 0) }, - { "Inno Setup Setup Data (5.3.8) (u)", INNO_VERSION_EXT(5, 3, 8, 0), true }, - { "Inno Setup Setup Data (5.3.9)", INNO_VERSION_EXT(5, 3, 9, 0) }, - { "Inno Setup Setup Data (5.3.9) (u)", INNO_VERSION_EXT(5, 3, 9, 0), true }, - { "Inno Setup Setup Data (5.3.10)", INNO_VERSION_EXT(5, 3, 10, 0) }, - { "Inno Setup Setup Data (5.3.10) (u)", INNO_VERSION_EXT(5, 3, 10, 0), true }, - { "Inno Setup Setup Data (5.4.2)", INNO_VERSION_EXT(5, 4, 2, 0) }, - { "Inno Setup Setup Data (5.4.2) (u)", INNO_VERSION_EXT(5, 4, 2, 0), true }, + { "Inno Setup Setup Data (1.3.21)", INNO_VERSION_EXT(1, 3, 21, 0), false }, + { "Inno Setup Setup Data (1.3.25)", INNO_VERSION_EXT(1, 3, 25, 0), false }, + { "Inno Setup Setup Data (2.0.0)", INNO_VERSION_EXT(2, 0, 0, 0), false }, + { "Inno Setup Setup Data (2.0.1)", INNO_VERSION_EXT(2, 0, 1, 0), false }, + { "Inno Setup Setup Data (2.0.5)", INNO_VERSION_EXT(2, 0, 5, 0), false }, + { "Inno Setup Setup Data (2.0.6a)", INNO_VERSION_EXT(2, 0, 6, 0), false }, + { "Inno Setup Setup Data (2.0.7)", INNO_VERSION_EXT(2, 0, 7, 0), false }, + { "Inno Setup Setup Data (2.0.8)", INNO_VERSION_EXT(2, 0, 8, 0), false }, + { "Inno Setup Setup Data (2.0.11)", INNO_VERSION_EXT(2, 0, 11, 0), false }, + { "Inno Setup Setup Data (2.0.17)", INNO_VERSION_EXT(2, 0, 17, 0), false }, + { "Inno Setup Setup Data (2.0.18)", INNO_VERSION_EXT(2, 0, 18, 0), false }, + { "Inno Setup Setup Data (3.0.0a)", INNO_VERSION_EXT(3, 0, 0, 0), false }, + { "Inno Setup Setup Data (3.0.1)", INNO_VERSION_EXT(3, 0, 1, 0), false }, + { "Inno Setup Setup Data (3.0.3)", INNO_VERSION_EXT(3, 0, 3, 0), false }, + { "Inno Setup Setup Data (3.0.5)", INNO_VERSION_EXT(3, 0, 5, 0), false }, + { "My Inno Setup Extensions Setup Data (3.0.6.1)", INNO_VERSION_EXT(3, 0, 6, 1), false }, + { "Inno Setup Setup Data (4.0.0a)", INNO_VERSION_EXT(4, 0, 0, 0), false }, + { "Inno Setup Setup Data (4.0.1)", INNO_VERSION_EXT(4, 0, 1, 0), false }, + { "Inno Setup Setup Data (4.0.3)", INNO_VERSION_EXT(4, 0, 3, 0), false }, + { "Inno Setup Setup Data (4.0.5)", INNO_VERSION_EXT(4, 0, 5, 0), false }, + { "Inno Setup Setup Data (4.0.9)", INNO_VERSION_EXT(4, 0, 9, 0), false }, + { "Inno Setup Setup Data (4.0.10)", INNO_VERSION_EXT(4, 0, 10, 0), false }, + { "Inno Setup Setup Data (4.0.11)", INNO_VERSION_EXT(4, 0, 11, 0), false }, + { "Inno Setup Setup Data (4.1.0)", INNO_VERSION_EXT(4, 1, 0, 0), false }, + { "Inno Setup Setup Data (4.1.2)", INNO_VERSION_EXT(4, 1, 2, 0), false }, + { "Inno Setup Setup Data (4.1.3)", INNO_VERSION_EXT(4, 1, 3, 0), false }, + { "Inno Setup Setup Data (4.1.4)", INNO_VERSION_EXT(4, 1, 4, 0), false }, + { "Inno Setup Setup Data (4.1.5)", INNO_VERSION_EXT(4, 1, 5, 0), false }, + { "Inno Setup Setup Data (4.1.6)", INNO_VERSION_EXT(4, 1, 6, 0), false }, + { "Inno Setup Setup Data (4.1.8)", INNO_VERSION_EXT(4, 1, 8, 0), false }, + { "Inno Setup Setup Data (4.2.0)", INNO_VERSION_EXT(4, 2, 0, 0), false }, + { "Inno Setup Setup Data (4.2.1)", INNO_VERSION_EXT(4, 2, 1, 0), false }, + { "Inno Setup Setup Data (4.2.2)", INNO_VERSION_EXT(4, 2, 2, 0), false }, + { "Inno Setup Setup Data (4.2.3)", INNO_VERSION_EXT(4, 2, 3, 0), false }, + { "Inno Setup Setup Data (4.2.5)", INNO_VERSION_EXT(4, 2, 5, 0), false }, + { "Inno Setup Setup Data (4.2.6)", INNO_VERSION_EXT(4, 2, 6, 0), false }, + { "Inno Setup Setup Data (5.0.0)", INNO_VERSION_EXT(5, 0, 0, 0), false }, + { "Inno Setup Setup Data (5.0.1)", INNO_VERSION_EXT(5, 0, 1, 0), false }, + { "Inno Setup Setup Data (5.0.3)", INNO_VERSION_EXT(5, 0, 3, 0), false }, + { "Inno Setup Setup Data (5.0.4)", INNO_VERSION_EXT(5, 0, 4, 0), false }, + { "Inno Setup Setup Data (5.1.0)", INNO_VERSION_EXT(5, 1, 0, 0), false }, + { "Inno Setup Setup Data (5.1.2)", INNO_VERSION_EXT(5, 1, 2, 0), false }, + { "Inno Setup Setup Data (5.1.7)", INNO_VERSION_EXT(5, 1, 7, 0), false }, + { "Inno Setup Setup Data (5.1.10)", INNO_VERSION_EXT(5, 1, 10, 0), false }, + { "Inno Setup Setup Data (5.1.13)", INNO_VERSION_EXT(5, 1, 13, 0), false }, + { "Inno Setup Setup Data (5.2.0)", INNO_VERSION_EXT(5, 2, 0, 0), false }, + { "Inno Setup Setup Data (5.2.1)", INNO_VERSION_EXT(5, 2, 1, 0), false }, + { "Inno Setup Setup Data (5.2.3)", INNO_VERSION_EXT(5, 2, 3, 0), false }, + { "Inno Setup Setup Data (5.2.5)", INNO_VERSION_EXT(5, 2, 5, 0), false }, + { "Inno Setup Setup Data (5.2.5) (u)", INNO_VERSION_EXT(5, 2, 5, 0), true }, + { "Inno Setup Setup Data (5.3.0)", INNO_VERSION_EXT(5, 3, 0, 0), false }, + { "Inno Setup Setup Data (5.3.0) (u)", INNO_VERSION_EXT(5, 3, 0, 0), true }, + { "Inno Setup Setup Data (5.3.3)", INNO_VERSION_EXT(5, 3, 3, 0), false }, + { "Inno Setup Setup Data (5.3.3) (u)", INNO_VERSION_EXT(5, 3, 3, 0), true }, + { "Inno Setup Setup Data (5.3.5)", INNO_VERSION_EXT(5, 3, 5, 0), false }, + { "Inno Setup Setup Data (5.3.5) (u)", INNO_VERSION_EXT(5, 3, 5, 0), true }, + { "Inno Setup Setup Data (5.3.6)", INNO_VERSION_EXT(5, 3, 6, 0), false }, + { "Inno Setup Setup Data (5.3.6) (u)", INNO_VERSION_EXT(5, 3, 6, 0), true }, + { "Inno Setup Setup Data (5.3.7)", INNO_VERSION_EXT(5, 3, 7, 0), false }, + { "Inno Setup Setup Data (5.3.7) (u)", INNO_VERSION_EXT(5, 3, 7, 0), true }, + { "Inno Setup Setup Data (5.3.8)", INNO_VERSION_EXT(5, 3, 8, 0), false }, + { "Inno Setup Setup Data (5.3.8) (u)", INNO_VERSION_EXT(5, 3, 8, 0), true }, + { "Inno Setup Setup Data (5.3.9)", INNO_VERSION_EXT(5, 3, 9, 0), false }, + { "Inno Setup Setup Data (5.3.9) (u)", INNO_VERSION_EXT(5, 3, 9, 0), true }, + { "Inno Setup Setup Data (5.3.10)", INNO_VERSION_EXT(5, 3, 10, 0), false }, + { "Inno Setup Setup Data (5.3.10) (u)", INNO_VERSION_EXT(5, 3, 10, 0), true }, + { "Inno Setup Setup Data (5.4.2)", INNO_VERSION_EXT(5, 4, 2, 0), false }, + { "Inno Setup Setup Data (5.4.2) (u)", INNO_VERSION_EXT(5, 4, 2, 0), true }, }; using std::cout; using std::string; @@ -140,7 +140,8 @@ void InnoVersion::load(std::istream & is) { if(legacyVersion[0] == 'i' && legacyVersion[sizeof(legacyVersion) - 1] == '\x1a') { - cout << "found legacy version: \"" << safestring(legacyVersion, sizeof(legacyVersion) - 1) << '"' << endl; + cout << "found legacy version: \"" + << safestring(legacyVersion, sizeof(legacyVersion) - 1) << '"' << endl; for(size_t i = 0; i < ARRAY_SIZE(knownLegacySetupDataVersions); i++) { if(!memcmp(legacyVersion, knownLegacySetupDataVersions[i].name, sizeof(legacyVersion))) { diff --git a/src/setup/Version.hpp b/src/setup/Version.hpp index d2d9f30..fe97893 100644 --- a/src/setup/Version.hpp +++ b/src/setup/Version.hpp @@ -15,24 +15,28 @@ struct InnoVersion { InnoVersionConstant version; - char bits; // 16 or 32 + uint8_t bits; // 16 or 32 bool unicode; bool known; - inline InnoVersion() : known(false) { }; + inline InnoVersion() : known(false) { } - inline InnoVersion(InnoVersionConstant _version, bool _unicode = false, bool _known = false, char _bits = 32) : version(_version), unicode(_unicode), known(_known), bits(_bits) { }; + inline InnoVersion(InnoVersionConstant _version, bool _unicode = false, + bool _known = false, uint8_t _bits = 32) + : version(_version), bits(_bits), unicode(_unicode), known(_known) { } - inline InnoVersion(char a, char b, char c, char d = 0, bool _unicode = false, bool _known = false, char _bits = 32) : version(INNO_VERSION_EXT(a, b, c, d)), unicode(_unicode), known(_known), bits(_bits) { }; + inline InnoVersion(uint8_t a, uint8_t b, uint8_t c, uint8_t d = 0, bool _unicode = false, + bool _known = false, uint8_t _bits = 32) + : version(INNO_VERSION_EXT(a, b, c, d)), bits(_bits), unicode(_unicode), known(_known) { } - inline int a() const { return version >> 24; } - inline int b() const { return (version >> 16) & 0xff; } - inline int c() const { return (version >> 8) & 0xff; } - inline int d() const { return version & 0xff; } + inline unsigned int a() const { return version >> 24; } + inline unsigned int b() const { return (version >> 16) & 0xff; } + inline unsigned int c() const { return (version >> 8) & 0xff; } + inline unsigned int d() const { return version & 0xff; } void load(std::istream & is); @@ -44,12 +48,24 @@ struct InnoVersion { }; -inline bool operator==(const InnoVersion & a, const InnoVersion & b) { return a.version == b.version; } -inline bool operator!=(const InnoVersion & a, const InnoVersion & b) { return !operator==(a, b); } -inline bool operator< (const InnoVersion & a, const InnoVersion & b) { return a.version < b.version; } -inline bool operator> (const InnoVersion & a, const InnoVersion & b) { return operator< (b, a); } -inline bool operator<=(const InnoVersion & a, const InnoVersion & b) { return !operator> (a, b); } -inline bool operator>=(const InnoVersion & a, const InnoVersion & b) { return !operator< (a, b); } +inline bool operator==(const InnoVersion & a, const InnoVersion & b) { + return a.version == b.version; +} +inline bool operator!=(const InnoVersion & a, const InnoVersion & b) { + return !operator==(a, b); +} +inline bool operator< (const InnoVersion & a, const InnoVersion & b) { + return a.version < b.version; +} +inline bool operator> (const InnoVersion & a, const InnoVersion & b) { + return operator< (b, a); +} +inline bool operator<=(const InnoVersion & a, const InnoVersion & b) { + return !operator> (a, b); +} +inline bool operator>=(const InnoVersion & a, const InnoVersion & b) { + return !operator< (a, b); +} inline bool operator==(const InnoVersion & a, InnoVersionConstant b) { return a.version == b; } inline bool operator!=(const InnoVersion & a, InnoVersionConstant b) { return !operator==(a, b); } diff --git a/src/setup/WindowsVersion.cpp b/src/setup/WindowsVersion.cpp index f2a3611..de66938 100644 --- a/src/setup/WindowsVersion.cpp +++ b/src/setup/WindowsVersion.cpp @@ -94,6 +94,7 @@ std::ostream & operator<<(std::ostream & os, const WindowsVersion::Version & v) if(v.build) { os << v.build; } + return os; } std::ostream & operator<<(std::ostream & os, const WindowsVersion & v) { @@ -117,10 +118,10 @@ std::ostream & operator<<(std::ostream & os, const WindowsVersion & v) { os << ')'; } if(v.ntServicePack.major || v.ntServicePack.minor) { - os << " service pack " << v.ntServicePack.major; - if(v.ntServicePack.minor) { - os << '.' << v.ntServicePack.minor; - } + os << " service pack " << v.ntServicePack.major; + if(v.ntServicePack.minor) { + os << '.' << v.ntServicePack.minor; + } } return os; } diff --git a/src/setup/WindowsVersion.hpp b/src/setup/WindowsVersion.hpp index 32a45b1..86d34ac 100644 --- a/src/setup/WindowsVersion.hpp +++ b/src/setup/WindowsVersion.hpp @@ -57,7 +57,7 @@ struct WindowsVersion { return !(*this == o); } - const static WindowsVersion none; + static const WindowsVersion none; }; diff --git a/src/stream/BlockFilter.hpp b/src/stream/BlockFilter.hpp index 9d46a3e..3e5cb63 100644 --- a/src/stream/BlockFilter.hpp +++ b/src/stream/BlockFilter.hpp @@ -52,13 +52,13 @@ public: std::streamsize nread = boost::iostreams::read(src, temp, sizeof(temp)); if(nread == EOF) { return false; - } else if(nread != sizeof(temp)) { + } else if(nread != sizeof(temp)) { throw block_error("unexpected block end"); } std::memcpy(&blockCrc32, temp, sizeof(blockCrc32)); blockCrc32 = LittleEndian::byteSwapIfAlien(blockCrc32); - length = boost::iostreams::read(src, buffer, sizeof(buffer)); + length = size_t(boost::iostreams::read(src, buffer, sizeof(buffer))); if(length == size_t(EOF)) { throw block_error("unexpected block end"); } @@ -78,7 +78,7 @@ public: template std::streamsize read(Source & src, char * dest, std::streamsize n) { - size_t read = 0; + std::streamsize read = 0; while(n) { if(pos == length && !read_chunk(src)) { @@ -89,7 +89,7 @@ public: std::copy(buffer + pos, buffer + pos + size, dest + read); - pos += size, n -= size, read += size; + pos += size_t(size), n -= size, read += size; } return read; diff --git a/src/stream/BlockReader.cpp b/src/stream/BlockReader.cpp index 8e9f0bc..e34b168 100644 --- a/src/stream/BlockReader.cpp +++ b/src/stream/BlockReader.cpp @@ -41,7 +41,7 @@ std::istream * BlockReader::get(std::istream & base, const InnoVersion & version Crc32 actualCrc; actualCrc.init(); - uint64_t storedSize; + uint32_t storedSize; BlockCompression compression; if(version >= INNO_VERSION(4, 0, 9)) { @@ -61,7 +61,7 @@ std::istream * BlockReader::get(std::istream & base, const InnoVersion & version } // Add the size of a CRC32 checksum for each 4KiB subblock. - storedSize += ceildiv(storedSize, 4096) * 4; + storedSize += uint32_t(ceildiv(storedSize, 4096) * 4); } if(actualCrc.finalize() != expectedCrc) { diff --git a/src/stream/ChecksumFilter.hpp b/src/stream/ChecksumFilter.hpp index 4632899..2cee364 100644 --- a/src/stream/ChecksumFilter.hpp +++ b/src/stream/ChecksumFilter.hpp @@ -29,7 +29,7 @@ public: std::streamsize nread = boost::iostreams::read(src, dest, n); if(nread != EOF) { - hasher->update(dest, nread); + hasher->update(dest, size_t(nread)); } return nread; diff --git a/src/stream/ChunkReader.cpp b/src/stream/ChunkReader.cpp index f205276..2b26123 100644 --- a/src/stream/ChunkReader.cpp +++ b/src/stream/ChunkReader.cpp @@ -5,7 +5,7 @@ const char chunkId[4] = { 'z', 'l', 'b', 0x1a }; -ChunkReader::Chunk::Chunk(size_t _firstSlice, size_t _chunkOffset, uint64_t _chunkSize, +ChunkReader::Chunk::Chunk(size_t _firstSlice, uint32_t _chunkOffset, uint64_t _chunkSize, bool _compressed, bool _encrypted) : firstSlice(_firstSlice), chunkOffset(_chunkOffset), chunkSize(_chunkSize), compressed(_compressed), encrypted(_encrypted) { } diff --git a/src/stream/ChunkReader.hpp b/src/stream/ChunkReader.hpp index ce04434..467d4b5 100644 --- a/src/stream/ChunkReader.hpp +++ b/src/stream/ChunkReader.hpp @@ -21,19 +21,20 @@ public: struct Chunk { size_t firstSlice; //!< Slice where the chunk starts. - size_t chunkOffset; //!< Offset of the compressed chunk in firstSlice. + uint32_t chunkOffset; //!< Offset of the compressed chunk in firstSlice. uint64_t chunkSize; //! Total compressed size of the chunk. bool compressed; bool encrypted; - Chunk(size_t firstSlice, size_t chunkOffset, uint64_t chunkSize, bool compressed, bool encrypted); + Chunk(size_t firstSlice, uint32_t chunkOffset, uint64_t chunkSize, + bool compressed, bool encrypted); bool operator<(const Chunk & o) const; bool operator==(const Chunk & o) const; }; - ChunkReader(uint64_t _storedSize) : storedSize(_storedSize) { } + explicit ChunkReader(uint64_t _storedSize) : storedSize(_storedSize) { } uint64_t chunkDecompressedBytesRead; uint64_t chunkCompressedBytesLeft; diff --git a/src/stream/InstructionFilter.hpp b/src/stream/InstructionFilter.hpp index bd07ad2..1a7de12 100644 --- a/src/stream/InstructionFilter.hpp +++ b/src/stream/InstructionFilter.hpp @@ -92,7 +92,7 @@ private: template std::streamsize call_instruction_decoder_4108::read(Source & src, char * dest, std::streamsize n) { - for(size_t i = 0; i < n; i++, addr_offset++) { + for(std::streamsize i = 0; i < n; i++, addr_offset++) { int byte = boost::iostreams::get(src); if(byte == EOF) { return i ? i : EOF; } @@ -107,13 +107,13 @@ std::streamsize call_instruction_decoder_4108::read(Source & src, char * dest, s } } else { - addr += byte; - byte = addr; + addr += uint8_t(byte); + byte = uint8_t(addr); addr >>= 8; addr_bytes_left--; } - *dest++ = uint8_t(byte); + *dest++ = char(uint8_t(byte)); } return n; @@ -127,16 +127,17 @@ std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, s //! Total number of filtered bytes read and written to dest. #define total_read (n - (end - dest)) -#define flush(N) { \ +#define flush(N) \ + { \ if((N) > 0) { \ flush_bytes = (N); \ size_t buffer_i = 0; \ do { \ if(dest == end) { \ - memmove(buffer, buffer + buffer_i, flush_bytes); \ + memmove(buffer, buffer + buffer_i, size_t(flush_bytes)); \ return total_read; \ } \ - *dest++ = buffer[buffer_i++]; \ + *dest++ = char(buffer[buffer_i++]); \ } while(--flush_bytes); \ } \ } (void)0 @@ -153,7 +154,7 @@ std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, s int byte = boost::iostreams::get(src); if(byte == EOF) { return total_read ? total_read : EOF; } if(byte == boost::iostreams::WOULD_BLOCK) { return total_read; } - *dest++ = byte; + *dest++ = char(byte); offset++; if(byte != 0xe8 && byte != 0xe9) { // Not a CALL or JMP instruction. @@ -175,10 +176,10 @@ std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, s char * dst = reinterpret_cast(buffer + 4 + flush_bytes); std::streamsize nread = boost::iostreams::read(src, dst, -flush_bytes); if(nread == EOF) { - flush(4 + flush_bytes); + flush(int8_t(4 + flush_bytes)); return total_read ? total_read : EOF; } - flush_bytes += nread, offset += nread; + flush_bytes = int8_t(flush_bytes + nread), offset += uint32_t(nread); if(flush_bytes) { return total_read; } // Verify that the high byte of the address is 0x00 or 00xff. @@ -196,7 +197,7 @@ std::streamsize call_instruction_decoder_5200::read(Source & src, char * dest, s // of the original relative address is likely to be the sign extension // of bit 23, so if bit 23 is set, toggle all bits in the high byte. if(rel & 0x800000) { - buffer[3] = ~buffer[3]; + buffer[3] = uint8_t(~buffer[3]); } } diff --git a/src/stream/LzmaFilter.cpp b/src/stream/LzmaFilter.cpp index 6a41a5f..fb7688f 100644 --- a/src/stream/LzmaFilter.cpp +++ b/src/stream/LzmaFilter.cpp @@ -18,7 +18,7 @@ static lzma_stream * init_raw_lzma_stream(lzma_vli filter, lzma_options_lzma & o *strm = tmp; strm->allocator = NULL; - const lzma_filter filters[2] = { { filter, &options }, { LZMA_VLI_UNKNOWN } }; + const lzma_filter filters[2] = { { filter, &options }, { LZMA_VLI_UNKNOWN, NULL } }; lzma_ret ret = lzma_raw_decoder(strm, filters); if(ret != LZMA_OK) { delete strm; @@ -35,14 +35,14 @@ bool lzma_decompressor_impl_base::filter(const char * & begin_in, const char * e lzma_stream * strm = static_cast(stream); strm->next_in = reinterpret_cast(begin_in); - strm->avail_in = end_in - begin_in; + strm->avail_in = size_t(end_in - begin_in); strm->next_out = reinterpret_cast(begin_out); - strm->avail_out = end_out - begin_out; + strm->avail_out = size_t(end_out - begin_out); lzma_ret ret = lzma_code(strm, LZMA_RUN); - begin_in = reinterpret_cast(strm->next_in); + begin_in = reinterpret_cast(strm->next_in); begin_out = reinterpret_cast(strm->next_out); if(ret != LZMA_OK && ret != LZMA_STREAM_END && ret != LZMA_BUF_ERROR) { @@ -77,7 +77,7 @@ bool inno_lzma1_decompressor_impl::filter(const char * & begin_in, const char * lzma_options_lzma options; - uint8_t properties = header[0]; + uint8_t properties = uint8_t(header[0]); if(properties > (9 * 5 * 5)) { throw lzma_error("inno lzma1 property error", LZMA_FORMAT_ERROR); } @@ -94,7 +94,7 @@ bool inno_lzma1_decompressor_impl::filter(const char * & begin_in, const char * } return lzma_decompressor_impl_base::filter(begin_in, end_in, begin_out, end_out, flush); -} +} bool inno_lzma2_decompressor_impl::filter(const char * & begin_in, const char * end_in, char * & begin_out, char * end_out, bool flush) { @@ -108,7 +108,7 @@ bool inno_lzma2_decompressor_impl::filter(const char * & begin_in, const char * lzma_options_lzma options; - uint8_t prop = *begin_in++; + uint8_t prop = uint8_t(*begin_in++); if(prop > 40) { throw lzma_error("inno lzma2 property error", LZMA_FORMAT_ERROR); } diff --git a/src/stream/LzmaFilter.hpp b/src/stream/LzmaFilter.hpp index d506e1c..09a475f 100644 --- a/src/stream/LzmaFilter.hpp +++ b/src/stream/LzmaFilter.hpp @@ -49,7 +49,7 @@ public: bool filter(const char * & begin_in, const char * end_in, char * & begin_out, char * end_out, bool flush); - inline void close() { lzma_decompressor_impl_base::close(); nread = 0; } + inline void close() { lzma_decompressor_impl_base::close(), nread = 0; } private: diff --git a/src/stream/SliceReader.cpp b/src/stream/SliceReader.cpp index 6edb474..5a6b0ac 100644 --- a/src/stream/SliceReader.cpp +++ b/src/stream/SliceReader.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "util/LoadingUtils.hpp" #include "util/Output.hpp" @@ -20,20 +21,20 @@ const char sliceIds[][8] = { } // anonymous namespace -SliceReader::SliceReader(const string & setupFile, size_t _dataOffset) +SliceReader::SliceReader(const string & setupFile, uint32_t _dataOffset) : dir(), lastDir(), baseFile(), dataOffset(_dataOffset), slicesPerDisk(1), currentSlice(0) { ifs.open(setupFile.c_str(), std::ios_base::binary | std::ios_base::in | std::ios_base::ate); - sliceSize = ifs.tellg(); - if(sliceSize < dataOffset) { + + sliceSize = uint32_t(std::min(ifs.tellg(), std::numeric_limits::max())); + if(ifs.seekg(dataOffset).fail()) { ifs.close(); - } else { - ifs.seekg(dataOffset); } } -SliceReader::SliceReader(const std::string & _dir, const std::string & _baseFile, size_t _slicesPerDisk) +SliceReader::SliceReader(const std::string & _dir, const std::string & _baseFile, + size_t _slicesPerDisk) : dir(_dir), lastDir(_dir), baseFile(_baseFile), dataOffset(0), slicesPerDisk(_slicesPerDisk), currentSlice(0) { } @@ -55,14 +56,14 @@ bool SliceReader::openFile(const std::string & file) { std::cout << color::cyan << "\33[2K\r[slice] opening " << file << color::reset << std::endl; - ifs.close();; + ifs.close(); ifs.open(file.c_str(), std::ios_base::in | std::ios_base::binary | std::ios_base::ate); if(ifs.fail()) { return false; } - size_t fileSize = ifs.tellg(); + std::streampos fileSize = ifs.tellg(); ifs.seekg(0); char magic[8]; @@ -85,7 +86,7 @@ bool SliceReader::openFile(const std::string & file) { } sliceSize = loadNumber(ifs); - if(ifs.fail() || sliceSize > fileSize) { + if(ifs.fail() || std::streampos(sliceSize) > fileSize) { LogError << "[slice] bad slice size: " << sliceSize << " > " << fileSize; ifs.close(); return false; @@ -146,7 +147,7 @@ bool SliceReader::open(size_t slice, const std::string & file) { return false; } -bool SliceReader::seek(size_t slice, size_t offset) { +bool SliceReader::seek(size_t slice, uint32_t offset) { if(!seek(slice)) { return false; @@ -165,9 +166,7 @@ bool SliceReader::seek(size_t slice, size_t offset) { std::streamsize SliceReader::read(char * buffer, std::streamsize bytes) { - size_t nread = 0; - - std::streamsize requested = bytes; + std::streamsize nread = 0; if(!seek(currentSlice)) { return nread; @@ -175,12 +174,12 @@ std::streamsize SliceReader::read(char * buffer, std::streamsize bytes) { while(bytes > 0) { - std::streamsize remaining = sliceSize - ifs.tellg(); + std::streamsize remaining = std::streamsize(sliceSize - size_t(ifs.tellg())); if(!remaining) { if(!seek(currentSlice + 1)) { return nread; } - remaining = sliceSize - ifs.tellg(); + remaining = std::streamsize(sliceSize - size_t(ifs.tellg())); } std::streamsize read = ifs.read(buffer, std::min(remaining, bytes)).gcount(); diff --git a/src/stream/SliceReader.hpp b/src/stream/SliceReader.hpp index 17cb4a0..fc8f23c 100644 --- a/src/stream/SliceReader.hpp +++ b/src/stream/SliceReader.hpp @@ -11,12 +11,12 @@ class SliceReader : public boost::iostreams::source { std::string dir; std::string lastDir; std::string baseFile; - const size_t dataOffset; + const uint32_t dataOffset; const size_t slicesPerDisk; size_t currentSlice; std::string sliceFile; - size_t sliceSize; + uint32_t sliceSize; std::ifstream ifs; @@ -25,7 +25,7 @@ class SliceReader : public boost::iostreams::source { public: - SliceReader(const std::string & setupFile, size_t dataOffset); + SliceReader(const std::string & setupFile, uint32_t dataOffset); /*! * if Ver>=4107 then baseFile := PathChangeExt(PathExtractName(SetupLdrOriginalFilename), '') @@ -33,7 +33,7 @@ public: */ SliceReader(const std::string & dir, const std::string & baseFile, size_t slicesPerDisk); - bool seek(size_t slice, size_t offset); + bool seek(size_t slice, uint32_t offset); std::streamsize read(char * buffer, std::streamsize bytes); diff --git a/src/util/Endian.hpp b/src/util/Endian.hpp index 65d3630..a59e817 100644 --- a/src/util/Endian.hpp +++ b/src/util/Endian.hpp @@ -9,25 +9,25 @@ inline uint8_t byteSwap(uint8_t value) { } inline int8_t byteSwap(int8_t value) { - return byteSwap(uint8_t(value)); + return int8_t(byteSwap(uint8_t(value))); } inline uint16_t byteSwap(uint16_t value) { #if defined(_MSC_VER) && _MSC_VER >= 1300 return _byteswap_ushort(value); #else - return (uint16_t(uint8_t(value)) << 8) | uint8_t(value >> 8); + return uint16_t((uint16_t(uint8_t(value)) << 8) | uint8_t(value >> 8)); #endif } inline int16_t byteSwap(int16_t value) { - return byteSwap(uint16_t(value)); + return int16_t(byteSwap(uint16_t(value))); } inline uint32_t byteSwap(uint32_t value) { #if defined(__GNUC__) return __builtin_bswap32(value); -#elif _MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400 || (_MSC_VER >= 1300 && !defined(_DLL))) return _byteswap_ulong(value); #else return (uint32_t(byteSwap(uint16_t(value))) << 16) | byteSwap(uint16_t(value >> 16)); @@ -35,7 +35,7 @@ inline uint32_t byteSwap(uint32_t value) { } inline int32_t byteSwap(int32_t value) { - return byteSwap(uint32_t(value)); + return int32_t(byteSwap(uint32_t(value))); } inline uint64_t byteSwap(uint64_t value) { @@ -49,7 +49,7 @@ inline uint64_t byteSwap(uint64_t value) { } inline int64_t byteSwap(int64_t value) { - return byteSwap(uint64_t(value)); + return int64_t(byteSwap(uint64_t(value))); } template @@ -103,9 +103,9 @@ struct BigEndian : public Endianness { }; -#ifdef BOOST_LITTLE_ENDIAN +#if defined(BOOST_LITTLE_ENDIAN) typedef LittleEndian NativeEndian; -#elif BOOLST_BIG_ENDIAN +#elif defined(BOOST_BIG_ENDIAN) typedef BigEndian NativeEndian; #else #error "Unsupported host endianness." diff --git a/src/util/Enum.hpp b/src/util/Enum.hpp index 4dd9a79..3771db5 100644 --- a/src/util/Enum.hpp +++ b/src/util/Enum.hpp @@ -31,7 +31,8 @@ struct EnumNames { #define ENUM_NAMES(Enum, Name, ...) \ const char * EnumNames::type>::name = (Name); \ const char * EnumNames::type>::names[] = { __VA_ARGS__ }; \ - const size_t EnumNames::type>::count = ARRAY_SIZE(EnumNames::type>::names); \ + const size_t EnumNames::type>::count = \ + ARRAY_SIZE(EnumNames::type>::names); \ std::ostream & operator<<(std::ostream & os, GetEnum::type value) { \ if(value < EnumNames::type>::count) { \ return os << EnumNames::type>::names[value]; \ diff --git a/src/util/Flags.hpp b/src/util/Flags.hpp index 1b8be2b..03142c3 100644 --- a/src/util/Flags.hpp +++ b/src/util/Flags.hpp @@ -10,7 +10,9 @@ // loosely based on QFlags from Qt template -struct EnumSize { private: static const size_t value = 42; }; +struct EnumSize { + static const size_t value; +}; /*! * A typesafe way to define flags as a combination of enum values. diff --git a/src/util/LoadingUtils.cpp b/src/util/LoadingUtils.cpp index cd29e7d..43c9ed8 100644 --- a/src/util/LoadingUtils.cpp +++ b/src/util/LoadingUtils.cpp @@ -37,12 +37,12 @@ iconv_t getConverter(uint32_t codepage) { void BinaryString::loadInto(std::istream & is, std::string & target) { - size_t length = loadNumber(is); - if(is.fail()) { + int32_t length = loadNumber(is); + if(is.fail() || length < 0) { return; } - target.resize(length); + target.resize(size_t(length)); is.read(&target[0], length); } diff --git a/src/util/LoadingUtils.hpp b/src/util/LoadingUtils.hpp index 5f849a0..109d590 100644 --- a/src/util/LoadingUtils.hpp +++ b/src/util/LoadingUtils.hpp @@ -7,6 +7,10 @@ #include #include #include + +#include +#include + #include "util/Endian.hpp" struct BinaryString { @@ -30,7 +34,8 @@ struct EncodedString { std::string & data; uint32_t codepage; - inline EncodedString(std::string & target, uint32_t _codepage) : data(target), codepage(_codepage) { } + inline EncodedString(std::string & target, uint32_t _codepage) + : data(target), codepage(_codepage) { } static void loadInto(std::istream & is, std::string & target, uint32_t codepage); @@ -61,24 +66,25 @@ inline T loadNumber(std::istream & is) { return LittleEndian::byteSwapIfAlien(load(is)); } -template ::is_signed> -struct compatible_integer { typedef void type; }; -template -struct compatible_integer { typedef uint8_t type; }; -template -struct compatible_integer { typedef int8_t type; }; -template -struct compatible_integer { typedef uint16_t type; }; -template -struct compatible_integer { typedef int16_t type; }; -template -struct compatible_integer { typedef uint32_t type; }; -template -struct compatible_integer { typedef int32_t type; }; -template -struct compatible_integer { typedef uint64_t type; }; -template -struct compatible_integer { typedef int64_t type; }; +template ::is_signed> +struct compatible_integer { + typedef void type; +}; + +template +struct compatible_integer { + typedef typename boost::uint_t< + boost::static_unsigned_min::value + >::exact type; +}; + +template +struct compatible_integer { + typedef typename boost::int_t< + boost::static_unsigned_min::value + >::exact type; +}; template T loadNumber(std::istream & is, size_t bits) { diff --git a/src/util/StoredEnum.hpp b/src/util/StoredEnum.hpp index 1f33f79..51741e6 100644 --- a/src/util/StoredEnum.hpp +++ b/src/util/StoredEnum.hpp @@ -98,14 +98,19 @@ public: inline std::bitset getBitSet() const { - static const size_t ulong_size = sizeof(unsigned long) * 8; + // Make `make style` shut up since we really need unsigned long here. + #define stored_enum_concat_(a, b, c, d) a##b c##d + typedef stored_enum_concat_(unsi, gned, lo, ng) ulong_type; + #undef stored_enum_concat_ + + static const size_t ulong_size = sizeof(ulong_type) * 8; BOOST_STATIC_ASSERT(base_size % ulong_size == 0 || base_size < ulong_size); std::bitset result(0); for(size_t i = 0; i < count; i++) { for(size_t j = 0; j < ceildiv(base_size, ulong_size); j++) { - result |= std::bitset(static_cast(bits[i] >> (j * ulong_size))) + result |= std::bitset(static_cast(bits[i] >> (j * ulong_size))) << ((i * base_size) + (j * ulong_size)); } } @@ -138,7 +143,8 @@ public: } if(bits) { - LogWarning << "unexpected " << EnumNames::name << " flags: " << std::hex << bits << std::dec; + LogWarning << "unexpected " << EnumNames::name << " flags: " + << std::hex << bits << std::dec; } return result; @@ -166,7 +172,7 @@ public: size_t bits; - StoredFlagReader(std::istream & _is) : is(_is), pos(0), result(0), bits(0) { }; + explicit StoredFlagReader(std::istream & _is) : is(_is), pos(0), result(0), bits(0) { } void add(enum_type flag) { @@ -195,7 +201,7 @@ class StoredFlagReader > : public StoredFlagReader { public: - StoredFlagReader(std::istream & is) : StoredFlagReader(is) { }; + explicit StoredFlagReader(std::istream & is) : StoredFlagReader(is) { } }; diff --git a/src/util/Utils.hpp b/src/util/Utils.hpp index 07d8495..25f05c7 100644 --- a/src/util/Utils.hpp +++ b/src/util/Utils.hpp @@ -2,6 +2,7 @@ #ifndef INNOEXTRACT_UTIL_UTILS_HPP #define INNOEXTRACT_UTIL_UTILS_HPP +#include #include #include @@ -23,9 +24,10 @@ struct Quoted { const std::string & str; - Quoted(const std::string & _str) : str(_str) { } + explicit Quoted(const std::string & _str) : str(_str) { } }; + inline std::ostream & operator<<(std::ostream & os, const Quoted & q) { color::shell_command prev = color::current; os << '"' << color::green; @@ -33,7 +35,8 @@ inline std::ostream & operator<<(std::ostream & os, const Quoted & q) { unsigned char c = (unsigned char)*i; if(c < ' ' && c != '\t' && c != '\r' && c != '\n') { std::ios_base::fmtflags old = os.flags(); - os << color::red << '<' << std::hex << std::setfill('0') << std::setw(2) << int(c) << '>' << color::green; + os << color::red << '<' << std::hex << std::setfill('0') << std::setw(2) + << int(c) << '>' << color::green; os.setf(old, std::ios_base::basefield); } else { os << *i; @@ -47,13 +50,16 @@ struct IfNotEmpty { const std::string & name; const std::string & value; - IfNotEmpty(const std::string & _name, const std::string & _value) : name(_name), value(_value) { } + IfNotEmpty(const std::string & _name, const std::string & _value) + : name(_name), value(_value) { } }; + inline std::ostream & operator<<(std::ostream & os, const IfNotEmpty & s) { if(s.value.length() > 100) { color::shell_command prev = color::current; - return os << s.name << ": " << color::white << s.value.length() << prev << " bytes" << std::endl; + return os << s.name << ": " << color::white << s.value.length() << prev + << " bytes" << std::endl; } else if(!s.value.empty()) { return os << s.name << ": " << Quoted(s.value) << std::endl; } else { @@ -68,9 +74,11 @@ struct _IfNot { const T value; const T excluded; - _IfNot(const std::string & _name, T _value, T _excluded) : name(_name), value(_value), excluded(_excluded) { } + _IfNot(const std::string & _name, T _value, T _excluded) + : name(_name), value(_value), excluded(_excluded) { } }; + template inline std::ostream & operator<<(std::ostream & os, const _IfNot & s) { if(s.value != s.excluded) { @@ -80,18 +88,20 @@ inline std::ostream & operator<<(std::ostream & os, const _IfNot & s) { return os; } } + template _IfNot IfNot(const std::string & name, T value, T excluded) { return _IfNot(name, value, excluded); } + template _IfNot IfNotZero(const std::string & name, T value) { return _IfNot(name, value, T(0)); } -template -inline A ceildiv(A num, B denom) { - return A((num + (denom - 1)) / denom); +template +inline T ceildiv(T num, T denom) { + return (num + (denom - T(1))) / denom; } template @@ -99,12 +109,13 @@ struct _PrintHex { T value; - _PrintHex(T data) : value(data) { } + explicit _PrintHex(T data) : value(data) { } bool operator==(const _PrintHex & o) const { return value == o.value; } bool operator!=(const _PrintHex & o) const { return value != o.value; } }; + template inline std::ostream & operator<<(std::ostream & os, const _PrintHex & s) { @@ -115,6 +126,7 @@ inline std::ostream & operator<<(std::ostream & os, const _PrintHex & s) { os.setf(old, std::ios_base::basefield); return os; } + template _PrintHex PrintHex(T value) { return _PrintHex(value); @@ -133,36 +145,38 @@ struct _PrintBytes { T value; - _PrintBytes(T data) : value(data) { } + explicit _PrintBytes(T data) : value(data) { } bool operator==(const _PrintBytes & o) const { return value == o.value; } bool operator!=(const _PrintBytes & o) const { return value != o.value; } }; + template inline std::ostream & operator<<(std::ostream & os, const _PrintBytes & s) { - int precision = os.precision(); + std::streamsize precision = os.precision(); - size_t frac = 0; - size_t whole = s.value; + size_t frac = size_t(1024 * (s.value - T(uint64_t(s.value)))); + uint64_t whole = uint64_t(s.value); size_t i = 0; - while((whole & ~0x3ff) && i < ARRAY_SIZE(byteSizeUnits) - 1) { + while(whole > 1024 && i < ARRAY_SIZE(byteSizeUnits) - 1) { - frac = (whole & 0x3ff), whole >>= 10; + frac = whole % 1024, whole /= 1024; i++; } - float num = whole + (frac / 1024.f); + float num = float(whole) + (float(frac) / 1024.f); os << std::setprecision(3) << num << ' ' << byteSizeUnits[i]; os.precision(precision); return os; } + template _PrintBytes PrintBytes(T value) { return _PrintBytes(value);