File: //bin/X11/svn_apply_autoprops
#!/usr/bin/python3
# To do:
# 1) Switch to using the Subversion Python bindings.
#
# $HeadURL: https://svn.apache.org/repos/asf/subversion/trunk/contrib/client-side/svn_apply_autoprops.py $
# $LastChangedRevision: 1741723 $
# $LastChangedDate: 2016-04-30 04:16:53 -0400 (Sat, 30 Apr 2016) $
# $LastChangedBy: stefan2 $
#
# Copyright (C) 2005,2006 Blair Zajac <blair@orcaware.com>
#
# This script is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# A copy of the GNU General Public License can be obtained by writing
# to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA.
import getopt
import fnmatch
import os
import re
import sys
# The default path to the Subversion configuration file.
SVN_CONFIG_FILENAME = os.path.expandvars('$HOME/.subversion/config')
# The name of Subversion's private directory in working copies.
SVN_WC_ADM_DIR_NAME = '.svn'
# The name this script was invoked as.
PROGNAME = os.path.basename(sys.argv[0])
def usage():
  print("""This script reads the auto-properties defined in the file
'%s'
and applies them recursively to all the files and directories in the
current working copy.  It may behave differently than the Subversion
command line; where the subversion command line may only apply a single
matching auto-property to a single pathname, this script will apply all
matching lines to a single pathname.
Usage:
  %s [options] [WC_PATH]
where WC_PATH is the path to a working copy.
If WC_PATH is not specified, '.' is assumed.
Valid options are:
  --help, -h         : Print this help text.
  --config ARG       : Read the Subversion config file at path ARG
                       instead of '%s'.
""" % (SVN_CONFIG_FILENAME, PROGNAME, SVN_CONFIG_FILENAME))
def get_autoprop_lines(fd):
  lines = []
  reading_autoprops = 0
  re_start_autoprops = re.compile('^\s*\[auto-props\]\s*')
  re_end_autoprops = re.compile('^\s*\[\w+\]\s*')
  for line in fd.xreadlines():
    if reading_autoprops:
      if re_end_autoprops.match(line):
        reading_autoprops = 0
        continue
    else:
      if re_start_autoprops.match(line):
        reading_autoprops = 1
        continue
    if reading_autoprops:
      lines += [line]
  return lines
def process_autoprop_lines(lines):
  result = []
  for line in lines:
    # Split the line on the = separating the fnmatch string from the
    # properties.
    try:
      (fnmatch, props) = line.split('=', 1)
    except ValueError:
      continue
    # Remove leading and trailing whitespace from the fnmatch and
    # properties.
    fnmatch = fnmatch.strip()
    props = props.strip()
    # Create a list of property name and property values.  Remove all
    # leading and trailing whitespce from the propery names and
    # values.
    props_list = []
    for prop in props.split(';'):
      prop = prop.strip()
      if not len(prop):
        continue
      try:
        (prop_name, prop_value) = prop.split('=', 1)
        prop_name = prop_name.strip()
        prop_value = prop_value.strip()
      except ValueError:
        prop_name = prop
        prop_value = '*'
      if len(prop_name):
        props_list += [(prop_name, prop_value)]
    result += [(fnmatch, props_list)]
  return result
def filter_walk(autoprop_lines, dirname, filenames):
  # Do not descend into a .svn directory.
  try:
    filenames.remove(SVN_WC_ADM_DIR_NAME)
  except ValueError:
    pass
  filenames.sort()
  # Find those filenames that match each fnmatch.
  for autoprops_line in autoprop_lines:
    fnmatch_str = autoprops_line[0]
    prop_list = autoprops_line[1]
    matching_filenames = fnmatch.filter(filenames, fnmatch_str)
    matching_filenames = [f for f in matching_filenames \
      if not os.path.islink(os.path.join(dirname, f))]
    if not matching_filenames:
      continue
    for prop in prop_list:
      command = ['svn', 'propset', prop[0], prop[1]]
      for f in matching_filenames:
        command += ["%s/%s" % (dirname, f)]
      status = os.spawnvp(os.P_WAIT, 'svn', command)
      if status:
        print('Command %s failed with exit status %s' \
              % (command, status))
def main():
  try:
    opts, args = getopt.getopt(sys.argv[1:], 'h', ['help', 'config='])
  except getopt.GetoptError as e:
    usage()
    return 1
  config_filename = None
  for (o, a) in opts:
    if o == '-h' or o == '--help':
      usage()
      return 0
    elif o == '--config':
      config_filename = os.path.abspath(a)
  if not config_filename:
    config_filename = SVN_CONFIG_FILENAME
  if len(args) == 0:
    wc_path = '.'
  elif len(args) == 1:
    wc_path = args[0]
  else:
    usage()
    print("Too many arguments: %s" % ' '.join(args))
    return 1
  try:
    fd = file(config_filename)
  except IOError:
    print("Cannot open svn configuration file '%s' for reading: %s" \
          % (config_filename, sys.exc_value.strerror))
    return 1
  autoprop_lines = get_autoprop_lines(fd)
  fd.close()
  autoprop_lines = process_autoprop_lines(autoprop_lines)
  os.path.walk(wc_path, filter_walk, autoprop_lines)
if __name__ == '__main__':
  sys.exit(main())