docs: def main(argv, stdout=sys.stdout, stderr=sys.stderr):
      """Main gitlint routine. To be called from scripts."""
      # Wrap sys stdout for python 2, so print can understand unicode.
      linesep = os.linesep
      if sys.version_info[0] < 3:
          if stdout == sys.stdout:
              stdout = codecs.getwriter("utf-8")(stdout)
          if stderr == sys.stderr:
              stderr = codecs.getwriter("utf-8")(stderr)
          linesep = unicode(os.linesep)  # pylint: disable=undefined-variable
  
      arguments = docopt.docopt(
          __doc__, argv=argv[1:], version='git-lint v%s' % __VERSION__)
  
      json_output = arguments['--json']
  
      vcs, repository_root = get_vcs_root()
  
      if vcs is None:
          stderr.write('fatal: Not a git repository' + linesep)
          return 128
  
      commit = None
      if arguments['--last-commit']:
          commit = vcs.last_commit()
  
      if arguments['FILENAME']:
          invalid_filenames = find_invalid_filenames(arguments['FILENAME'],
                                                     repository_root)
          if invalid_filenames:
              invalid_filenames.append(('', ''))
              stderr.write(
                  linesep.join(invalid[1] for invalid in invalid_filenames))
              return 2
  
          changed_files = vcs.modified_files(
              repository_root,
              tracked_only=arguments['--tracked'],
              commit=commit)
          modified_files = {}
          for filename in arguments['FILENAME']:
              normalized_filename = os.path.abspath(filename)
              modified_files[normalized_filename] = changed_files.get(
                  normalized_filename)
      else:
          modified_files = vcs.modified_files(
              repository_root,
              tracked_only=arguments['--tracked'],
              commit=commit)
  
      linter_not_found = False
      files_with_problems = 0
      gitlint_config = get_config(repository_root)
      json_result = {}
  
      with futures.ThreadPoolExecutor(max_workers=multiprocessing.cpu_count())\
              as executor:
          processfile = functools.partial(process_file, vcs, commit,
                                          arguments['--force'], gitlint_config)
          for filename, result in executor.map(
                  processfile, [(filename, modified_files[filename])
                                for filename in sorted(modified_files.keys())]):
  
              rel_filename = os.path.relpath(filename)
  
              if not json_output:
                  stdout.write('Linting file: %s%s' % (termcolor.colored(
                      rel_filename, attrs=('bold', )), linesep))
  
              output_lines = []
              if result.get('error'):
                  output_lines.extend('%s: %s' % (ERROR, reason)
                                      for reason in result.get('error'))
                  linter_not_found = True
              if result.get('skipped'):
                  output_lines.extend('%s: %s' % (SKIPPED, reason)
                                      for reason in result.get('skipped'))
              if not result.get('comments', []):
                  if not output_lines:
                      output_lines.append(OK)
              else:
                  files_with_problems += 1
                  for data in result['comments']:
                      formatted_message = format_comment(data)
                      output_lines.append(formatted_message)
                      data['formatted_message'] = formatted_message
  
              if json_output:
                  json_result[filename] = result
              else:
                  output = linesep.join(output_lines)
                  stdout.write(output)
                  stdout.write(linesep + linesep)
  
      if json_output:
          # Hack to convert to unicode, Python3 returns unicode, wheres Python2
          # returns str.
          stdout.write(
              json.dumps(json_result,
                         ensure_ascii=False).encode('utf-8').decode('utf-8'))
  
      if files_with_problems > 0:
          return 1
      if linter_not_found:
          return 4
      return 0