rdiff-backup-users
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[rdiff-backup-users] PATCHES for Cross-Platform-Compatibility


From: Toscano2
Subject: [rdiff-backup-users] PATCHES for Cross-Platform-Compatibility
Date: Thu, 27 Nov 2008 09:11:57 -0500

In my intial posting about 'Feature Suggestions for 
Cross-Platform-Compatibility' 
I wrote about some feature enhancements (I think) would benefit rdiff-backup 
while 
keeping full backward compatibility:
http://www.backupcentral.com/phpBB2/two-way-mirrors-of-external-mailing-lists-3/rdiff-backup-23/feature-suggestions-for-cross-platform-compatibility-93227/

Now this functionality is implemented and (well-)tested locally on Windows XP 
NTFS 
and Linux EXT2+EXT3.

During learning Python and reviewing the rdiff-backup code I fixed two small 
bugs 
in unrelated modules and cleaned up/updated the man-page.

BUGS Fixed:
- module eas_acls.py, Line: 224: comment position was erroneously calculated 
before,
  resulting in a (potential) nasty bug: truncating AccessControlLists
- module fs_abilities.py, get_ctq_from_fsas():
  WAS pot. bug before, because unconditionally only ';' was quoted,
  even if overridden with another character

Here are the patches, first the modifications to the man/help-page to explain
most of the reasons and added functionality:


--- rdiff-backup/rdiff-backup.1   2008-08-20 
02:37:41.000000000 +0000
+++ rdiff-backup-mycode/rdiff-backup.1   2008-11-09 
11:40:58.000000000 +0000
@@ -1,4 +1,4 @@
-.TH RDIFF-BACKUP 1 "JULY 2007" "Version 1.1.13" "User Manuals" \" -*- nroff -*-
+.TH RDIFF-BACKUP 1  "OCTOBER 2008" "Version 1.2.2" "User Manuals" \" -*- 
nroff -*-
 .SH NAME
 rdiff-backup \- local/remote mirror and incremental backup
 .SH SYNOPSIS
@@ -344,7 +344,7 @@
 rdiff-backup-data directory.  rdiff-backup will run slightly quicker
 and take up a bit less space.
 .TP
-.BI \-\-no-hard-links
+.B \-\-no-hard-links
 Don't replicate hard links on destination side.  If many hard-linked
 files are present, this option can drastically decrease memory usage.
 This option is enabled by default if the backup source or restore
@@ -369,6 +369,15 @@
 .B \-\-override-chars-to-quote 
 If the filesystem to which we are backing up is not case-sensitive, 
automatic 'quoting' of characters occurs. For example, a file 'Developer.doc' 
will be converted into ';068eveloper.doc'. To override this behavior, you need 
to specify this option.
 .TP
+.BI "\-\-override-quote-chars-and-fsabilities-from-file " filename
+This option lets you override ANY filesystem attributes and characters to 
quote, read from a config file.
+Similar to above \-\-override-chars-to-quote, but offers advanced 
functionality, you can specify INCLUDES, EXCLUDES,
+each also as a range of values (e.g. \\0x00-\\0x30) and supports 
decimal/hexadecimal and octal values.
+Use this option to create a single cross-platform-compatibility file, when you 
use several OS with different filesystems,
+then all filenames are converted properly to be 
+.B completely cross-platform 
+compatible. See below for a file-format description and example.
+.TP
 .B \-\-preserve-numerical-ids
 If set, rdiff-backup will preserve uids/gids instead of trying to
 preserve unames and gnames.  See the
@@ -485,6 +494,28 @@
 in the following host::filename argument(s).  The 
filename section
 will be ignored.
 .TP
+.B \-\-use-compatible-timestamps
+When \-\-use-compatible-timestamps is enabled, the above timestamp
+is created as following: "2008-10-13T04-09-38-07-00" (instead
+of default: "2008-10-13T04:09:38-07:00"). This format is 
allowed
+also on Windows filesystems where colons (":") are disallowed in
+filenames! If you want to locally backup a Unix path and later use
+.BR rsync (1)
+to backup to Microsoft Windows system, use this option!
+This option is fail-safe, you can continue your 'traditional' timestamped 
backups, change to this option
+later and even revert to the original, non-windows-compatible timestamp or 
even intermix different timestamp formats.
+.TP
+.B \-\-use-utc
+When \-\-use-utc is enabled, the timestamp is always stored as UTC, 
+indicated by appended "Z" (for Zulu) instead of timezone 
+offset, thus becomes "2001-07-15T04-09-38Z". Using \-\-use-utc 
+is recommended when 1) a remote backup is performed, 2) source
+and destination for backup/restore are in different timezones, 3)
+you want to keep yourself from 'daylight saving' errors, 4) all
+timestamped filenames are 5 characters shorter.
+This option is fail-safe, you can continue your 'traditional' timestamped 
backups, change to this option
+later and even revert to the original, non-windows-compatible timestamp or 
even intermix different timestamp formats.
+.TP
 .BI "\-\-user-mapping-file " filename
 Map user names and ids according to the user mapping file
 .IR filename .
@@ -577,6 +608,28 @@
 http://www.w3.org/TR/NOTE-datetime.  Basically they look like
 "2001-07-15T04:09:38-07:00", which means what it looks 
like.  The
 "-07:00" section means the time zone is 7 hours behind UTC.
+When 
+.B \-\-use-compatible-timestamps
+is enabled, the above timestamp is created as following: 
"2001-07-15T04-09-38-07-00". This format
+is allowed also on Windows filesystems where ":" colons are disallowed in 
filenames!
+This option is fail-safe, you can continue your 'traditional' timestamped 
backups, change to this option
+later and even revert to the original, non-windows-compatible timestamp or 
even intermix any.
+When additionally
+.B \-\-use-utc
+is enabled, then the timestamp is
+always used as UTC, indicated by appended "Z" (for Zulu) instead 
+of timezone offset, thus becomes "2001-07-15T04-09-38Z". See above option 
details for benefits.
+This option is fail-safe, you can continue your 'traditional' timestamped 
backups, change to this option
+later and even revert to the original, non-windows-compatible timestamp or 
even intermix them.
+
+Using
+.B \-\-use-utc
+is recommended when 1) a remote backup is performed, 2) source
+and destination for backup/restore are in different timezones, 3)
+you want to keep yourself from 'daylight saving' problems, 4) all
+timestamped filenames are 5 characters shorter.
+This option is fail-safe, you can continue your 'traditional' timestamped 
backups including timezone-info,
+then use this option at any time, even intermixed.
 .PP
 Secondly, the
 .BI \-r , " \-\-restore-as-of" ", and " \-\-remove-older-than
@@ -697,6 +750,7 @@
 .BR \-\-include-globbing-filelist ,
 .BR \-\-include-globbing-filelist-stdin ,
 .BR \-\-include-filelist-stdin ,
+.BR \-\-override-quote-chars-and-fsabilities-from-file,
 and
 .BR \-\-include-regexp .
 Each file selection condition either matches or doesn't match a given
@@ -920,6 +974,53 @@
 the same as specifying "\-\-include dir/foo \-\-include dir/bar 
\-\-exclude **"
 on the command line.
 
+.B "\-\---override-quote-chars-and-fsabilities-from-file override_cfg.txt"
+.RE This option lets you override ANY filesystem attributes and characters to 
quote, read from a config file.
+Similar to above \-\-override-chars-to-quote, but offers advanced 
functionality, you can specify INCLUDES, EXCLUDES,
+each also as a range of values (\\0x00-\\0x30) and supports 
decimal/hexadecimal and octal values.
+Use this option to create a single cross-platform-compatibility file, when you 
use several OS with different filesystems,
+then all filenames are converted properly to be 
+.B completely cross-platform 
+compatible.
+.RE
+.B File-Format:
+.RE ########################################################
+ # Format description: escaped ('\\'-prefixed) numbers,
+ # supported are octal/hexadecimal/decimal values (even 
+ # inter-mixed), separated by semicolon ';'
+ # or specifying a character-range with a hyphen ('-') 
+ # (e.g. \\0x00-\\0x1F). For help in conversion from characters
+ # to decimal/hexadecimal/octal values, see URLs:
+ # http://www.asciitable.com/
+ # http://www.asciitable.de/tabelle.html
+ ###########################################################
+ INCLUDE:\\0x00-\\0x1F;\\0x22;\\0x2A;\\0x2F;\\0x3A;\\0x3C;\\0x3E;\
+\\0x3F;\\0x5C;\\0x7C;\\0x7F;\\0x3B
+ # Description of above INCLUDE: Quote non-printable char-range 
+ # decimal 0-31, and also quote&#58; ", *, /, &#58;, <, >, ?, \, |, 
+ # and 127 &#40;decimal; hex&#58; 7F&#41; &#40;DEL&#41; and ';' quotation
+ # char itself. This is required for Windows-compatibility.
+ EXCLUDE&#58;
+.RE
+ ############################################################
+ # FS-ABILITIES&#58; nameformat as in Python source-code, with 
+ # '+' and '-' prefix to indicate enabling, resp. disabling a 
+ # feature, separated by ';' from following other options.
+ # If you do not want an auto-detected filesystem-ability to 
+ # be overwritten, simply remove the option from below list.
+ #
+ ## TODO&#58; interaction and precedence from commandline with&#58;
+ # --exclude-special-files &#40;device-files;fifos;sockets;symbolic-links&#41;;
+ # --exclude-other-filesystems
+.RE
+ ###########
+ FS-ABILITIES&#58;+case_sensitive;+eas;+acls;+win_acls; \
+ +escape_dos_devices;-resource_forks;-carbonfile;-hardlinks; \
+ -fsync_dirs;-change_ownership;-high_perms;-symlink_perms;
+ ############################################################
+.RE
+
+.BR
&nbsp;Finally, the
&nbsp;.B \-\-include-regexp
&nbsp;and




diff U3 eas_acls.py eas_acls.py
--- eas_acls.py&nbsp; &nbsp;Sat Sep 27 01&#58;08&#58;31 2008
+++ eas_acls.py&nbsp; &nbsp;Sat Nov 01 11&#58;15&#58;45 2008
@@ -224,7 +224,7 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Set self.entry_list and 
self.default_entry_list from text"""
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.entry_list, self.default_entry_list = 
&#91;&#93;, &#91;&#93;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for line in text.split&#40;'\n'&#41;&#58;
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;comment_pos = text.find&#40;'#'&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;comment_pos = line.find&#40;'#'&#41; ## 
AFAICT was BUG before&#58; comment_pos = text.find&#40;'#'&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if comment_pos >= 0&#58; line = 
line&#91;&#58;comment_pos&#93;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;line = line.strip&#40;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if not line&#58; continue
diff U3 FilenameMapping.py FilenameMapping.py
--- FilenameMapping.py&nbsp; &nbsp;Sat Jan 05 19&#58;43&#58;13 2008
+++ FilenameMapping.py&nbsp; &nbsp;Thu Oct 23 14&#58;04&#58;21 2008
@@ -65,6 +65,7 @@
&nbsp;def init_quoting_regexps&#40;&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Compile quoting regular expressions"""
&nbsp;&nbsp; &nbsp;global chars_to_quote_regexp, unquoting_regexp
+
&nbsp;&nbsp; &nbsp;assert chars_to_quote and type&#40;chars_to_quote&#41; is 
types.StringType, \
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"Chars to quote&#58; '%s'" % 
&#40;chars_to_quote,&#41;
&nbsp;&nbsp; &nbsp;try&#58;
diff U3 fs_abilities.py fs_abilities.py
--- fs_abilities.py&nbsp; &nbsp;Wed Oct 08 00&#58;45&#58;42 2008
+++ fs_abilities.py&nbsp; &nbsp;Tue Nov 04 13&#58;11&#58;27 2008
@@ -27,7 +27,7 @@
&nbsp;
&nbsp;"""
&nbsp;
-import errno, os
+import errno, os, re, string
&nbsp;import Globals, log, TempFile, selection, robust, SetConnections, \
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;static, FilenameMapping, win_acls
&nbsp;
@@ -73,7 +73,7 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert 
boolean == 0
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;val_text 
= 'Off'
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;addline&#40;desc, 
val_text&#41;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;addline&#40;desc, 
val_text&#41;
&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;def get_title_line&#40;&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Add the first line, mostly for 
decoration"""
@@ -164,6 +164,9 @@
&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;subdir.delete&#40;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return self
+&nbsp; &nbsp;&nbsp; &nbsp;
+#&nbsp; &nbsp;def show_active_fsabilities&#40;self, &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;
&nbsp;
&nbsp;&nbsp; &nbsp;def set_ownership&#40;self, testdir&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Set self.ownership to true iff testdir's 
ownership can be changed"""
@@ -556,6 +559,11 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;% 
&#40;subdir.path&#41;, 4&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.escape_dos_devices = 1
&nbsp;
+
+## OM&#58; Todo&#58; Move function 'update_fs_abilities' herein?
+#&nbsp; &nbsp;def update_fs_abilities&#40;self, key, value&#41;&#58;
+###### END OF update_fs_abilities&#40;&#41;
+
&nbsp;def get_readonly_fsa&#40;desc_string, rp&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Return an fsa with given description_string
&nbsp;
@@ -633,6 +641,9 @@
&nbsp;
&nbsp;class BackupSetGlobals&#40;SetGlobals&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Functions for setting fsa related globals for backup 
session"""
+&nbsp; &nbsp;
+#----------------------------------------------------------------------
+&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;def update_triple&#40;self, src_support, dest_support, 
attr_triple&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Many of the settings have a common form we 
can handle here"""
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;active_attr, write_attr, conn_attr = attr_triple
@@ -645,6 +656,8 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;SetConnections.UpdateGlobal&#40;write_attr, 1&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;self.out_conn.Globals.set_local&#40;conn_attr, 1&#41;
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;&nbsp; &nbsp;def set_must_escape_dos_devices&#40;self, rbdir&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""If local edd or src edd, then must escape """
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;try&#58;
@@ -657,21 +670,29 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"Backup&#58; 
must_escape_dos_devices = %d" % \
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;&#40;self.src_fsa.escape_dos_devices or local_edd&#41;, 4&#41;
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;&nbsp; &nbsp;def set_chars_to_quote&#40;self, rbdir, force&#41;&#58;
-&nbsp; &nbsp;&nbsp; &nbsp;"""Set chars_to_quote setting for backup session
+&nbsp; &nbsp;&nbsp; &nbsp;"""Set chars_to_quote setting for backup session. 
Unlike the other options, 
+&nbsp; &nbsp;&nbsp; &nbsp;the chars_to_quote setting also depends on the 
current settings in the 
+&nbsp; &nbsp;&nbsp; &nbsp;rdiff-backup-data directory, not just the current fs 
features.
+&nbsp; &nbsp;&nbsp; &nbsp;"""
&nbsp;
-&nbsp; &nbsp;&nbsp; &nbsp;Unlike the other options, the chars_to_quote setting 
also
-&nbsp; &nbsp;&nbsp; &nbsp;depends on the current settings in the 
rdiff-backup-data
-&nbsp; &nbsp;&nbsp; &nbsp;directory, not just the current fs features.
+&nbsp; &nbsp;&nbsp; &nbsp;ctq = &#91;&#93;
&nbsp;
-&nbsp; &nbsp;&nbsp; &nbsp;"""
-&nbsp; &nbsp;&nbsp; &nbsp;&#40;ctq, update&#41; = 
self.compare_ctq_file&#40;rbdir,
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;self.get_ctq_from_fsas&#40;&#41;, force&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;if 
Globals.is_not_None&#40;'override_chars_to_quote'&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;wanted_ctq = 
self.get_ctq_and_fsabilities_from_file&#40; Globals.get&#40;'path_octqff'&#41; 
&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;else&#58; wanted_ctq = 
self.get_ctq_from_fsas&#40;&#41;
+
+# WAS&#58;&nbsp; &nbsp;&nbsp; &nbsp;&#40;ctq, update&#41; = 
self.compare_ctq_file&#40;rbdir, self.get_ctq_from_fsas&#40;&#41;, force&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&#40;ctq, update&#41; = 
self.compare_ctq_file&#40;rbdir, wanted_ctq, force&#41;
&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;SetConnections.UpdateGlobal&#40;'chars_to_quote', ctq&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if Globals.chars_to_quote&#58; 
FilenameMapping.set_init_quote_vals&#40;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return update
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;&nbsp; &nbsp;def get_ctq_from_fsas&#40;self&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Determine chars_to_quote just from 
filesystems, no ctq file"""
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ctq = &#91;&#93;
@@ -684,12 +705,276 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if self.dest_fsa.win_reserved_filenames&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if 
self.dest_fsa.extended_filenames&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;ctq.append&#40;'\000-\037'&#41; # Quote 0 - 31
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;# Quote ", *, /, &#58;, <, >, ?, \, |, 
and 127 &#40;DEL&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;# Quote ", *, /, &#58;, <, >, ?, \, |, 
and 127 &#40;decimal&#41; &#40;DEL&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;ctq.append&#40;'\"*/&#58;<>?\\\\|\177'&#41;
&nbsp;
-&nbsp; &nbsp;&nbsp; &nbsp;if ctq&#58; ctq.append&#40;';'&#41; # Quote quoting 
char if quoting anything
+# OM&#58; WAS pot. bug before, because unconditionally only ';' was quoted, 
even if overridden with another character
+#&nbsp; &nbsp;&nbsp; &nbsp;if ctq&#58; ctq.append&#40;';'&#41; # Quote quoting 
char if quoting anything
+&nbsp; &nbsp;&nbsp; &nbsp;if ctq&#58; 
ctq.append&#40;Globals.get&#40;'quoting_char'&#41;&#41; # Quote defined 
quoting-char if quoting anything
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return "".join&#40;ctq&#41;
&nbsp;
+#########################################################################################
+## OM-test
+## TODO&#58; Should be a combination of filesystem-detected requirements ?!?
+## Safe-version&#58; use both detected by FS-capabilities AND from file => 
most sensible
+## 
+## TODO&#58; enhance also overriding filesystem-detected capabilities; e.g.
+## use case-sensitivity even on case-insensitive FS, no hardlinking even if 
supported, etc.
+## impl. here also! +Option, -Option
+##
+## TODO&#58; later impl. option, if filesystem &#40;SRC, DEST or 
intermediary!&#41; is case-insensitive,
+## do simple 'sliding window' test for each directory to find any potential 
collision AND escape
+## only these. While writing this, a '--dry-run' option performing this action 
would be great,
+## add value to this software and positively advocates on file-naming!
+## TODO&#58; find any existing project/code to do this! There are only some 
regexp required for this!
+
+
+&nbsp; &nbsp;def get_ctq_and_fsabilities_from_file&#40;self, 
filepath_ctq&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;"""Determine chars_to_quote just from file, 
overriding all other settings"""
+
+&nbsp; &nbsp;&nbsp; &nbsp;ctq, ctq_includes, ctq_excludes, custom_fs_abilities 
= &#91;&#93;, &#91;&#93;, &#91;&#93;, &#91;&#93;
+
+&nbsp; &nbsp;&nbsp; &nbsp;try&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;file_in = open&#40;filepath_ctq, 
'r'&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;except IOError&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"Error&#58; Could not open 
'override-quoted-chars-and-fsabilities'-file '%s'!" % &#40;filepath_ctq&#41;, 
1&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for line in 
file_in.readlines&#40;&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;line = line.strip&#40;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if not line&#58; 
continue&nbsp; &nbsp; ## skip empty lines
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; 
get_ctq_from_file&#40;&#41;&#58; line read&#58; '%s'\n" % line,&nbsp; 6&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if re.match&#40;"^#", 
line&#41;&#58; ## skip the format description/examples + commented out lines
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;log.Log&#40;"DEBUG&#58; get_ctq_from_file&#40;&#41;&#58; comment 
skipped\n", 6&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;continue
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif 
re.match&#40;"^FS-ABILITIES&#58;&#40;.*?&#41;$", line&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;m = 
re.match&#40;"^FS-ABILITIES&#58;&#40;.*?&#41;$", line&#41; ## How to include 
the assignment 'm = ' right in above line?
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;custom_fs_abilities = m.group&#40;1&#41;.strip&#40;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;log.Log&#40;"DEBUG&#58; get_ctq_from_file&#40;&#41;&#58; 
FS-ABILITIES&#58; Filesystem overrides&#58; '%s'\n" % &#40; custom_fs_abilities 
&#41;, 6&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;self.override_fs_abilities&#40;custom_fs_abilities&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;m = 
re.match&#40;"^&#40;IN|EX&#41;CLUDE&#58;&#40;.*?&#41;$", line&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if 
m.group&#40;1&#41; == 'IN'&#58; ctq_includes = 
self.decode_characters&#40;m.group&#40;2&#41;.strip&#40;&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif 
m.group&#40;1&#41; == 'EX'&#58; ctq_excludes = 
self.decode_characters&#40;m.group&#40;2&#41;.strip&#40;&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;log.Log&#40;"DEBUG&#58; get_ctq_from_file&#40;&#41;&#58; %sCLUDE 
contents&#58; '%s'\n" % &#40;m.group&#40;1&#41;, m.group&#40;2&#41;&#41;, 6&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;file_in.close&#40;&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;ctq_decimals = self.diff_ctqff&#40;ctq_includes, 
ctq_excludes&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;result = self.collapse_ctqff&#40;ctq_decimals&#41;
+
+#&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"Number of includes&#58; '%d', of 
excludes '%d'" % &#40; len&#40;ctq_includes&#41;, len&#40;ctq_excludes&#41; 
&#41;, 6&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; final DIFF-array of decimal 
character values &#40;range only 0-255&#41;&#58; ", ctq_decimals
+&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; Final list of chars to 
quote&#58; '%s'\n" % result, 6&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;return result
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def decode_characters&#40;self, encoded_string&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;""" 
+&nbsp; &nbsp;&nbsp; &nbsp;Reads characters in multiple formats 
&#40;decimal/hexadecimal/octal&#41;, expands
+&nbsp; &nbsp;&nbsp; &nbsp;any ranges provided and converts/unifies all to 
decimals for later easy array-processing.
+&nbsp; &nbsp;&nbsp; &nbsp;Attention&#58; only allows ASCII &#40;0-255&#41;, no 
Unicode-points at this time!
+
+&nbsp; &nbsp;&nbsp; &nbsp;For help in conversion from/to 
decimal/hexadecimal/octal values, see URLs&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;http&#58;//www.asciitable.com/
+&nbsp; &nbsp;&nbsp; &nbsp;http&#58;//www.asciitable.de/tabelle.html
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+
+&nbsp; &nbsp;&nbsp; &nbsp;final_chars_to_quote_list = &#91;&#93;
+
+&nbsp; &nbsp;&nbsp; &nbsp;for item in encoded_string.split&#40;';'&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if not item&#58; continue
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; part '%s'" % &#40; 
item &#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;sublist = item.split&#40;'-'&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if &#40;len&#40;sublist&#41; != 
2&#41;&#58; ## is a number-representation of a single character only
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;replaced_number = 
string.replace&#40; sublist&#91;0&#93;, '\\', ''&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;## Get the number from 
whatever Base-/Radix was specified, either decimal/hexadecimal or octal
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;char_number = 
int&#40;replaced_number, 0&#41; ## read + convert to decimal/base10
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;final_chars_to_quote_list.append&#40; char_number &#41;
+## For debugging&#58;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if &#40;char_number <= 
255&#41;&#58; char = chr&#40;char_number&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; char = 
unichr&#40;char_number&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; Single 
char appended&#58; decimal '%d' as char '%s'" % &#40; char_number, char &#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; ## a range of characters 
specified, represented as numbers
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;replaced_number0 = 
string.replace&#40; sublist&#91;0&#93;, '\\', ''&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;replaced_number1 = 
string.replace&#40; sublist&#91;1&#93;, '\\', ''&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;char_number0 = 
int&#40;replaced_number0, 0&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;char_number1 = 
int&#40;replaced_number1, 0&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;final_chars_to_quote_list.extend&#40;range&#40;char_number0, char_number1 
+ 1&#41;&#41; ## '+1' because range&#40;&#41; excludes second parameter
+
+## For debugging&#58;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if &#40;char_number0 <= 
255&#41;&#58; char0 = chr&#40;char_number0&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; char0 = 
unichr&#40;char_number0&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if &#40;char_number1 <= 
255&#41;&#58; char1 = chr&#40;char_number1&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; char1 = 
unichr&#40;char_number1&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; Char 
range appended&#58; decimal '%d' - '%d' => '%s' - '%s'" % &#40;char_number0, 
char_number1, char0, char1&#41;
+
+#&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; char-array of decimal values&#58; 
", final_chars_to_quote_list
+&nbsp; &nbsp;&nbsp; &nbsp;return final_chars_to_quote_list
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def diff_ctqff&#40;self, list_includes, list_excludes&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;""" Actually find all decimal character values to 
quote, with any excluded values removed."""
+&nbsp; &nbsp;&nbsp; &nbsp;list_includes.sort&#40;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;list_excludes.sort&#40;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;final_list_ctqff = &#91;item for item in 
list_includes if item not in list_excludes&#93;
+&nbsp; &nbsp;&nbsp; &nbsp;return final_list_ctqff
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def collapse_ctqff&#40;self, listing&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+&nbsp; &nbsp;&nbsp; &nbsp;Collapses/compresses **more than two** consecutive 
integer numbers into a range&#58; e.g. 0 1 2 3 4 8 10 11 => 0-4,8,10,11
+&nbsp; &nbsp;&nbsp; &nbsp;Problem&#58; This is important or else a bug is 
thrown when quoted chars in range 0-31 decimal are given &#40;which is required 
for Windows FSs&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;of type&#58; "TypeError&#58; 'NoneType' object is 
not iterable"
+&nbsp; &nbsp;&nbsp; &nbsp;Solution&#58; The char-range 0-31 decimal must be 
written to 'quote'-file with '-' as a range, as in default quote-file for 
Windows FSs.
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+
+&nbsp; &nbsp;&nbsp; &nbsp;final_string, str_list, quoting_char_included = '', 
&#91;&#93;, 0
+&nbsp; &nbsp;&nbsp; &nbsp;quoting_integer_number = 
ord&#40;Globals.get&#40;'quoting_char'&#41;&#41; ## If the quoting char itself 
is forgotten from the list, add it! 
+
+## OM&#58; Remark&#58; this works for now, but 'feels' not ideal ... please 
improve.
+&nbsp; &nbsp;&nbsp; &nbsp;accumulator, start, stop, iter = -1, -1, -1, -1
+
+&nbsp; &nbsp;&nbsp; &nbsp;for item in listing&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;iter = iter + 1
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; iterating item '%s' 
- number '%d'" % &#40;item, iter&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if &#40;&#40;quoting_char_included == 
0&#41; and &#40;item == quoting_integer_number&#41;&#41;&#58; 
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;quoting_char_included = 1
+
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if iter == 0&#58; start = item; 
accumulator = 0; continue
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif &#40;listing&#91;iter&#93; == 
&#40;listing&#91;iter - 1&#93; + 1&#41;&#41;&#58;&nbsp; ## consecutive 
integers, store
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
consecutive numbers found '%r'-'%r'\n" % &#40;listing&#91;iter&#93;-1, 
listing&#91;iter&#93;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;stop = item; accumulator = 
accumulator + 1
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif accumulator >= 2&#58;&nbsp; ## 
more than two consecutive integers, store
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
accumulator '%s'\n" % &#40;chr&#40;start&#41; + '-' + chr&#40;stop&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;str_list.append&#40;chr&#40;start&#41; + '-' + chr&#40;stop&#41;&#41;;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;accumulator = 0; start = 
item
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif accumulator >= 1&#58;&nbsp; ## 
only two consecutive integers, do not store as range, but simple append both
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
accumulator '%s'\n" % &#40;chr&#40;start&#41; + chr&#40;stop&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;str_list.append&#40;chr&#40;start&#41; + chr&#40;stop&#41;&#41;;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;accumulator = 0; start = 
item
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;accumulator = 0; appendix 
= chr&#40;start&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;str_list.append&#40;appendix&#41;; start = item
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
ELSE-clause, solitaire char only '%s' appended\n" % appendix
+&nbsp; &nbsp;&nbsp; &nbsp;## END for-loop
+&nbsp; &nbsp;&nbsp; &nbsp;str_list.append&#40;chr&#40;start&#41;&#41;&nbsp; ## 
Last character needs to be appended, too!
+
+&nbsp; &nbsp;&nbsp; &nbsp;# Add defined quoting-char to existing list if not 
already included
+&nbsp; &nbsp;&nbsp; &nbsp;if &#40;str_list and &#40;quoting_char_included == 
0&#41;&#41;&#58; str_list.append&#40;Globals.get&#40;'quoting_char'&#41;&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;return "".join&#40;str_list&#41;
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def override_fs_abilities&#40;self, string_fs_abilities&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;""" Attention&#58; Only use when you know what you 
are doing, since this might break backups. """
+
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+#OM&#58; FS-ABILITIES&#58; nameformat as in Python-code
+#FS-ABILITIES&#58;+case_sensitivity;+eas;+acls;+win_acls;+escape_dos_devices;-resource_forks;-carbonfile;-hardlinks;-fsync_dirs;-change_ownership;-high_perms;-symlink_perms;
+## How about&#58; --exclude-special-files &#40;--exclude-device-files; 
--exclude-fifos;--exclude-sockets;--exclude-symbolic-links&#41;; 
--exclude-other-filesystems
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+
+&nbsp; &nbsp;&nbsp; &nbsp;fs_abilities_enabled, fs_abilities_disabled = 
&#91;&#93;, &#91;&#93;
+
+&nbsp; &nbsp;&nbsp; &nbsp;for item in 
string_fs_abilities.split&#40;';'&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if not item&#58; continue
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; item '%s'" % &#40; 
item &#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;m = 
re.match&#40;"^&#40;\+|-&#41;&#40;.*?&#41;$", item&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if&nbsp; &nbsp;&nbsp; 
m.group&#40;1&#41; == '+'&#58; 
fs_abilities_enabled.append&#40;m.group&#40;2&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif&nbsp; m.group&#40;1&#41; == 
'-'&#58; fs_abilities_disabled.append&#40;m.group&#40;2&#41;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; log.Log&#40;"Error! There's a 
'+' or '-' prefix missing from item '%s' in fs_abilities override file, fix 
this first!" % &#40;m.group&#40;2&#41;&#41;, 6&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;## END FOR-LOOP
+
+#&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; fs_abilities&nbsp; ENABLED&#58; 
", fs_abilities_enabled, " - DISABLED&#58; ", fs_abilities_disabled
+
+&nbsp; &nbsp;&nbsp; &nbsp;for item in fs_abilities_enabled&#58;&nbsp; 
self.update_fs_abilities&#40;item, 1&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;for item in fs_abilities_disabled&#58; 
self.update_fs_abilities&#40;item, 0&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+&nbsp; &nbsp;&nbsp; &nbsp;TODO&#58; interaction with the corresponding 
commandline options?
+Globals.set&#40;"never_drop_acls", 1&#41; ; 
Globals.set&#40;"carbonfile_active", 0&#41; ; Globals.set&#40;"acls_active", 
0&#41;
+Globals.set&#40;"win_acls_active", 0&#41; ; 
Globals.set&#40;'preserve_hardlinks', 0&#41; ; 
Globals.set&#40;'resource_forks_active', 0&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;"""
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def info_updated_fsa&#40;self, key, value&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
update_fs_abilities&#40;src,dest&#41;&#58; '%s' was changed to '%s'." % 
&#40;key, value&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;Globals.set&#40;'override_fsabilities', 1&#41;
+
+#----------------------------------------------------------------------
+
+&nbsp; &nbsp;def update_fs_abilities&#40;self, key, value&#41;&#58;
+
+#&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
update_fs_abilities&#40;&#41;&#58; processing '%s'" % key
+
+## OM&#58; AIM&#58; Wanted to create/lookup variable from string/key&#58;
+# I tried below to no avail with and without 'self.' prefix&#58;
+#&nbsp; &nbsp;&nbsp; &nbsp;src_key = 'self.src_fsa.' + key
+#&nbsp; &nbsp;&nbsp; &nbsp;src_key = vars&#40;&#41;&#91;'self.src_fsa.' + 
key&#93;
+#&nbsp; &nbsp;&nbsp; &nbsp;src_key = locals&#40;&#41;&#91;'self.src_fsa.' + 
key&#93;
+#&nbsp; &nbsp;&nbsp; &nbsp;src_key = globals&#40;&#41;&#91;'self.src_fsa.' + 
key&#93;
+#&nbsp; &nbsp;&nbsp; &nbsp;eval&#40;src_key = 'self.src_fsa.' + key&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;exec&#40; "src_key = 'self.src_fsa.' + key"&#41;
+#
+## OM&#58; This is an UGLY KLUDGE/workaround for now. AIM&#58; Wanted to 
create/lookup variable from string/key&#58;
+#&nbsp; &nbsp;&nbsp; &nbsp;src_key = "self.src_fsa." + key; print 
"Src-key&#58; '%s'\n" % src_key
+
+&nbsp; &nbsp;&nbsp; &nbsp;fsa_attribute_changed = 1
+
+&nbsp; &nbsp;&nbsp; &nbsp;if &#40; &#40;key.find&#40; 'extended_filenames' 
&#41; != -1&#41; and &#40; self.src_fsa.extended_filenames != value &#41; 
&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.extended_filenames = 
self.dest_fsa.extended_filenames = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 
'win_reserved_filenames' &#41; != -1&#41; and &#40; 
self.src_fsa.win_reserved_filenames != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.win_reserved_filenames = 
self.dest_fsa.win_reserved_filenames = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'case_sensitive' &#41; 
!= -1&#41; and &#40;self.src_fsa.case_sensitive != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.case_sensitive = 
self.dest_fsa.case_sensitive = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'ownership' &#41; != 
-1&#41; and &#40;self.src_fsa.ownership != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.ownership = 
self.dest_fsa.ownership = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'eas' &#41; != -1&#41; 
and &#40;self.src_fsa.eas != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.eas = self.dest_fsa.eas = 
value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'win_acls' &#41; != 
-1&#41; and &#40;self.src_fsa.win_acls != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.win_acls = 
self.dest_fsa.win_acls = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'acls' &#41; != 
-1&#41; and &#40;self.src_fsa.acls != value&#41; &#41;&#58; ### IMPORTANT! 
'acls' check must be after 'win_acls' check!
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.acls = self.dest_fsa.acls 
= value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'hardlinks' &#41; != 
-1&#41; and &#40;self.src_fsa.hardlinks != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.hardlinks = 
self.dest_fsa.hardlinks = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'fsync_dirs' &#41; != 
-1&#41; and &#40;self.src_fsa.fsync_dirs != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.fsync_dirs = value ## Not 
needed&#58; self.dest_fsa.fsync_dirs
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'dir_inc_perms' &#41; 
!= -1&#41; and &#40;self.src_fsa.dir_inc_perms != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.dir_inc_perms = 
self.dest_fsa.dir_inc_perms = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'resource_forks' &#41; 
!= -1&#41; and &#40;self.src_fsa.resource_forks != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.resource_forks = 
self.dest_fsa.resource_forks = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'carbonfile' &#41; != 
-1&#41; and &#40;self.src_fsa.carbonfile != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.carbonfile = 
self.dest_fsa.carbonfile = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'high_perms' &#41; != 
-1&#41; and &#40;self.src_fsa.high_perms != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.high_perms = 
self.dest_fsa.high_perms = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'escape_dos_devices' 
&#41; != -1&#41; and &#40;self.src_fsa.escape_dos_devices != value&#41; 
&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.escape_dos_devices_dirs = 
self.dest_fsa.escape_dos_devices = value
+&nbsp; &nbsp;&nbsp; &nbsp;elif &#40; &#40;key.find&#40; 'symlink_perms' &#41; 
!= -1&#41; and &#40;self.src_fsa.symlink_perms != value&#41; &#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.src_fsa.symlink_perms = 
self.dest_fsa.symlink_perms = value
+&nbsp; &nbsp;&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;fsa_attribute_changed = 0
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
update_fs_abilities&#40;&#41;&#58; Option '%s' either not changed or not 
recognized - skipped!" % key
+
+&nbsp; &nbsp;&nbsp; &nbsp;if &#40;fsa_attribute_changed != 0&#41;&#58; 
self.info_updated_fsa&#40;key, value&#41;
+
+## OM&#58; END of UGLY KLUDGE/workaround.
+
+#----------------------------------------------------------------------
+
+#########################################################################################
+## END OM-test
+
&nbsp;&nbsp; &nbsp;def compare_ctq_file&#40;self, rbdir, suggested_ctq, 
force&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Compare ctq file with suggested result, 
return actual ctq"""
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ctq_rp = rbdir.append&#40;"chars_to_quote"&#41;
@@ -733,9 +1018,11 @@
&nbsp;repository from the old quoting chars to the new ones.""" %
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&#40;suggested_ctq, actual_ctq, 
ctq_rp.path&#41;&#41;
&nbsp;
+#----------------------------------------------------------------------
&nbsp;
&nbsp;class RestoreSetGlobals&#40;SetGlobals&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Functions for setting fsa-related globals for restore 
session"""
+
&nbsp;&nbsp; &nbsp;def update_triple&#40;self, src_support, dest_support, 
attr_triple&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Update global settings for feature based on 
fsa results
&nbsp;
@@ -754,6 +1041,8 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.out_conn.Globals.set_local&#40;write_attr, 
1&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if src_support&#58; 
self.in_conn.Globals.set_local&#40;conn_attr, 1&#41;
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;&nbsp; &nbsp;def set_must_escape_dos_devices&#40;self, rbdir&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""If local edd or src edd, then must escape """
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if getattr&#40;self, "src_fsa", None&#41; is 
not None&#58;
@@ -769,6 +1058,8 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"Restore&#58; 
must_escape_dos_devices = %d" % \
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&#40;src_edd or 
local_edd&#41;, 4&#41;
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;&nbsp; &nbsp;def set_chars_to_quote&#40;self, rbdir&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Set chars_to_quote from rdiff-backup-data 
dir"""
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if Globals.chars_to_quote is not None&#58; 
return # already overridden
@@ -781,9 +1072,11 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;"assuming no quoting in backup repository.", 2&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;SetConnections.UpdateGlobal&#40;"chars_to_quote", ""&#41;
&nbsp;
+#----------------------------------------------------------------------
&nbsp;
&nbsp;class SingleSetGlobals&#40;RestoreSetGlobals&#41;&#58;
&nbsp;&nbsp; &nbsp;"""For setting globals when dealing only with one 
filesystem"""
+
&nbsp;&nbsp; &nbsp;def __init__&#40;self, conn, fsa&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.conn = conn
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.dest_fsa = fsa
@@ -816,6 +1109,7 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;self.update_triple&#40;self.dest_fsa.carbonfile,
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; &#40;'carbonfile_active', 
'carbonfile_write', 'carbonfile_conn'&#41;&#41;
&nbsp;
+#----------------------------------------------------------------------
&nbsp;
&nbsp;def backup_set_globals&#40;rpin, force&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Given rps for source filesystem and repository, set fsa 
globals
@@ -848,6 +1142,10 @@
&nbsp;&nbsp; &nbsp;if update_quoting and force&#58;
&nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;FilenameMapping.update_quoting&#40;Globals.rbdir&#41;
&nbsp;
+&nbsp; &nbsp;if Globals.is_not_None&#40;'override_fsabilities'&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;print "Changed 
Filesystem-Attributes&#58;\nSRC-FSA&#58;\n", src_fsa, "\nDEST-FSA&#58;\n", 
dest_fsa
+
+#----------------------------------------------------------------------
&nbsp;
&nbsp;def restore_set_globals&#40;rpout&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Set fsa related globals for restore session, given in/out 
rps"""
@@ -873,6 +1171,8 @@
&nbsp;&nbsp; &nbsp;rsg.set_escape_dos_devices&#40;&#41;
&nbsp;&nbsp; &nbsp;rsg.set_must_escape_dos_devices&#40;Globals.rbdir&#41;
&nbsp;
+#----------------------------------------------------------------------
+
&nbsp;def single_set_globals&#40;rp, read_only = None&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Set fsa related globals for operation on single 
filesystem"""
&nbsp;&nbsp; &nbsp;if read_only&#58;
@@ -894,3 +1194,4 @@
&nbsp;&nbsp; &nbsp;ssg.set_escape_dos_devices&#40;&#41;
&nbsp;&nbsp; &nbsp;ssg.set_must_escape_dos_devices&#40;Globals.rbdir&#41;
&nbsp;
+#----------------------------------------------------------------------
diff U3 Globals.py Globals.py
--- Globals.py&nbsp; &nbsp;Wed Jul 02 19&#58;03&#58;23 2008
+++ Globals.py&nbsp; &nbsp;Tue Nov 04 12&#58;51&#58;56 2008
@@ -23,12 +23,15 @@
&nbsp;
&nbsp;
&nbsp;# The current version of rdiff-backup
-version = "$version"
+version = "1.2.1 &#40;branched from cvs_r1312&#41; adapted OM." ## WAS&#58; 
"$version"
&nbsp;
&nbsp;# If this is set, use this value in seconds as the current time
&nbsp;# instead of reading it from the clock.
&nbsp;current_time = None
&nbsp;
+# Stores the local timezone for later UTC-conversion
+local_timezone = None
+
&nbsp;# This determines how many bytes to read at a time when copying
&nbsp;blocksize = 131072
&nbsp;
@@ -158,6 +161,27 @@
&nbsp;# should be escaped &#40;see FilenameMapping for more info&#41;.
&nbsp;chars_to_quote = None
&nbsp;quoting_char = ';'
+
+##############################################################################
+## OM-test
+## If set, the timestamps use the following format&#58; 
"2008-09-01T04-49-04-07-00" 
+## &#40;instead of "2008-09-01T04&#58;49&#58;04-07&#58;00"&#41;. This creates 
truly cross-platform
+## timestamps, e.g. required for transferring rdiff-backup archive to Windows 
filesystems.
+use_compatible_timestamps = None
+
+## if set, uses the characters/ranges for overriding local + evg. remote 
filesystem attributes
+## detected. Useful when you need to transfer a locally done backup later to 
another &#40;e.g. Windows&#41; filesystem.
+## Specify in this file either the characters you want to replace or their 
octal values + ranges &#40;e.g. '\000-\030'&#41;.
+override_chars_to_quote = None
+path_octqff = None
+override_fsabilities = None
+## TODO&#58; merge with above chars_to_quote var?!?
+
+## If set, use UTC as base for all file-access/modification calculations. 
Removes the explicit 
+## timezone-offset, and appends 'Z' for Zulu to the timestamp, resulting in 
"2008-09-01T04-49-04-Z"
+use_utc = None
+## END OM-test
+##############################################################################
&nbsp;
&nbsp;# If true, emit output intended to be easily readable by a
&nbsp;# computer.&nbsp; False means output is intended for humans.
diff U3 Main.py Main.py
--- Main.py&nbsp; &nbsp;Sun Oct 12 16&#58;46&#58;00 2008
+++ Main.py&nbsp; &nbsp;Sun Nov 02 14&#58;34&#58;31 2008
@@ -85,8 +85,11 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "remove-older-than=", "restore-as-of=", 
"restrict=",
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "restrict-read-only=", 
"restrict-update-only=", "server",
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "ssh-no-compression", "tempdir=", 
"terminal-verbosity=",
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "test-server", "user-mapping-file=", 
"verbosity=", "verify",
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "verify-at-time=", "version"&#93;&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "test-server",
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;## OM-test
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "use-utc", "use-compatible-timestamps", 
"override-quote-chars-and-fsabilities-from-file=", 
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ## END
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; "user-mapping-file=", "verbosity=", "verify", 
"verify-at-time=", "version"&#93;&#41;
&nbsp;&nbsp; &nbsp;except getopt.error, e&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;commandline_error&#40;"Bad commandline 
options&#58; " + str&#40;e&#41;&#41;
&nbsp;
@@ -146,6 +149,25 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;select_opts.append&#40;&#40;"--include-globbing-filelist",
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"standard input"&#41;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;select_files.append&#40;sys.stdin&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;## OM-test
+&nbsp; &nbsp;&nbsp; &nbsp;## Advantages&#58; --use-utc&#58; local/remote 
systems in different timezones do not cause problems
+&nbsp; &nbsp;&nbsp; &nbsp;elif opt == 
"--override-quote-chars-and-fsabilities-from-file"&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;p_octqff = arg
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;Globals.set_integer&#40;'override_chars_to_quote', 1&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Globals.set&#40;'path_octqff', 
p_octqff&#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;print "DEBUG&#58; 
override_chars_to_quote-from-file '%s'" % p_octqff
+
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;BackupSetGlobals.get_ctq_from_file&#40; 
Globals.get&#40;'path_octqff'&#41; &#41;
+#&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;## was&#58; 
fs_abilities.get_ctq_from_file&#40;&#41;
+# REM&#58; required also? Globals.set&#40;'chars_to_quote', arg&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;elif opt == "--use-utc"&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Globals.set&#40;"use_utc", 1&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;elif opt == "--use-compatible-timestamps"&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;Globals.set&#40;"use_compatible_timestamps", 1&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;## END OM-test
+
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif opt == "--include-regexp"&#58; 
select_opts.append&#40;&#40;opt, arg&#41;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elif opt == "--list-at-time"&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;restore_timestr, action = arg, 
"list-at-time"
@@ -240,8 +262,7 @@
&nbsp;&nbsp; &nbsp;else&#58; action = "backup"
&nbsp;
&nbsp;def commandline_error&#40;message&#41;&#58;
-&nbsp; &nbsp;Log.FatalError&#40;message + "\nSee the rdiff-backup manual page 
for "
-&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"more 
information."&#41;
+&nbsp; &nbsp;Log.FatalError&#40;message + "\nSee the rdiff-backup manual page 
for more information."&#41;
&nbsp;
&nbsp;def misc_setup&#40;rps&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Set default change ownership flag, umask, relay regexps"""
@@ -476,6 +497,9 @@
&nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;Log.open_logfile&#40;Globals.rbdir.append&#40;"backup.log"&#41;&#41;
&nbsp;&nbsp; &nbsp;checkdest_if_necessary&#40;rpout&#41;
&nbsp;&nbsp; &nbsp;prevtime = backup_get_mirrortime&#40;&#41;
+&nbsp; &nbsp;
+&nbsp; &nbsp;Log&#40;"DEBUG&#58; prevtime is %s" % prevtime, 6&#41;
+&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;if prevtime >= Time.curtime&#58; Log.FatalError&#40;
&nbsp;"""Time of Last backup is not in the past.&nbsp; This is probably caused
&nbsp;by running two backups in less than a second.&nbsp; Wait a second a try 
again."""&#41;
diff U3 metadata.py metadata.py
--- metadata.py&nbsp; &nbsp;Sat Sep 27 01&#58;17&#58;24 2008
+++ metadata.py&nbsp; &nbsp;Tue Oct 21 10&#58;08&#58;28 2008
@@ -542,10 +542,13 @@
&nbsp;
&nbsp;&nbsp; &nbsp;def _writer_helper&#40;self, prefix, flatfileclass, typestr, 
time&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Used in the get_xx_writer functions, returns 
a writer class"""
+&nbsp; &nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if time is None&#58; timestr = Time.curtimestr
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; timestr = 
Time.timetostring&#40;time&#41;&nbsp; &nbsp;&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;filename = '%s.%s.%s' % &#40;prefix, timestr, 
typestr&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;rp = Globals.rbdir.append&#40;filename&#41;
+
+&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; created filename '%s'\n" % 
filename, 6&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert not rp.lstat&#40;&#41;, "File %s already 
exists!" % &#40;rp.path,&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert rp.isincfile&#40;&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return flatfileclass&#40;rp, 'w', callback = 
self.add_incrp&#41;
diff U3 rpath.py rpath.py
--- rpath.py&nbsp; &nbsp;Sun Oct 12 16&#58;46&#58;00 2008
+++ rpath.py&nbsp; &nbsp;Tue Oct 21 14&#58;09&#58;51 2008
@@ -352,10 +352,14 @@
&nbsp;&nbsp; &nbsp;assert rpath.conn is Globals.local_connection
&nbsp;&nbsp; &nbsp;return open&#40;rpath.path, "rb"&#41;
&nbsp;
+## This fn is erroneous? No! This returns the number of tokens!!
&nbsp;def get_incfile_info&#40;basename&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Returns None or tuple of 
&nbsp;&nbsp; &nbsp;&#40;is_compressed, timestr, type, and basename&#41;"""
&nbsp;&nbsp; &nbsp;dotsplit = basename.split&#40;"."&#41;
+
+#&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; get_incfile_info&#40;%s&#41;&#58; 
dotsplit&#58; '%d'" % &#40;basename, len&#40;dotsplit&#41; &#41;, 6&#41;
+
&nbsp;&nbsp; &nbsp;if dotsplit&#91;-1&#93; == "gz"&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;compressed = 1
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if len&#40;dotsplit&#41; < 4&#58; return None
@@ -364,7 +368,13 @@
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;compressed = None
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if len&#40;dotsplit&#41; < 3&#58; return None
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;timestring, ext = dotsplit&#91;-2&#58;&#93;
+
+&nbsp; &nbsp;result_time = Time.stringtotime&#40;timestring&#41;
+#&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; timestring&#58; '%s' - ext '%s' - 
resulting timestring '%s'" % &#40;timestring, ext, result_time &#41;, 6&#41;
+
&nbsp;&nbsp; &nbsp;if Time.stringtotime&#40;timestring&#41; is None&#58; return 
None
+&nbsp; &nbsp;
+&nbsp; &nbsp;
&nbsp;&nbsp; &nbsp;if not &#40;ext == "snapshot" or ext == "dir" or
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ext == "missing" or ext == "diff" 
or ext == "data"&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return None
@@ -1181,12 +1191,15 @@
&nbsp;
&nbsp;&nbsp; &nbsp;def isincfile&#40;self&#41;&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""Return true if path looks like an increment 
file
-
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Also sets various inc information used by the 
*inc* functions.
-
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"""
+
+#&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; inside isincfile&#40;&#41; 
beginning", 9&#41;
+
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if self.index&#58; basename = 
self.index&#91;-1&#93;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else&#58; basename = self.base
+
+#&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; basename is '%s'" % 
basename, 6&#41;
&nbsp;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;inc_info = get_incfile_info&#40;basename&#41;
&nbsp;
diff U3 Time.py Time.py
--- Time.py&nbsp; &nbsp;Thu Jan 25 04&#58;09&#58;16 2007
+++ Time.py&nbsp; &nbsp;Tue Nov 04 12&#58;50&#58;55 2008
@@ -20,8 +20,9 @@
&nbsp;"""Provide time related exceptions and functions"""
&nbsp;
&nbsp;import time, types, re, sys, calendar
+import os ## For timezone-change
&nbsp;import Globals
-
+import log
&nbsp;
&nbsp;class TimeException&#40;Exception&#41;&#58; pass
&nbsp;
@@ -60,11 +61,40 @@
&nbsp;&nbsp; &nbsp;global prevtime, prevtimestr
&nbsp;&nbsp; &nbsp;prevtime, prevtimestr = timeinseconds, timestr
&nbsp;
+
+## OM&#58; currently testing which version is more cross-platform 
compatible&#58; either this fn. or time.gmtime&#40;&#41;-call
+def set_zimezone_to_utc&#40;reset_to_localtime_again=False&#41;&#58;
+&nbsp; &nbsp;"""Alternative way to set timezone to UTC and reset it again to 
local timezone for all timestamps"""
+&nbsp; &nbsp;if reset_to_localtime_again&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;os.environ&#91;'TZ'&#93; = 
Globals.get&#40;local_timezone&#41;&nbsp; &nbsp;## 
os.environ&#91;'TZ'&#93;='Europe/Berlin'
+&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;Globals.set&#40;'local_timezone', time.tzname&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;os.environ&#91;'TZ'&#93;=''&nbsp; &nbsp;### is UTC 
now
+
+&nbsp; &nbsp;time.tzset&#40;&#41;
+&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; timezone used now '%s' - before was&#58; 
'%s' " % &#40;time.tzname, Globals.get&#40;local_timezone&#41; &#41;, 7&#41;
+
+
&nbsp;def timetostring&#40;timeinseconds&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Return w3 datetime compliant listing of timeinseconds"""
-&nbsp; &nbsp;s = time.strftime&#40;"%Y-%m-%dT%H&#58;%M&#58;%S", 
time.localtime&#40;timeinseconds&#41;&#41;
+&nbsp; &nbsp;"""OR else use the combatible version e.g. for Windows"""
+
+&nbsp; &nbsp;if Globals.is_not_None&#40;'use_compatible_timestamps'&#41;&#58; 
time_formatstring = "%Y-%m-%dT%H-%M-%S"
+&nbsp; &nbsp;else&#58; time_formatstring = "%Y-%m-%dT%H&#58;%M&#58;%S"
+
+&nbsp; &nbsp;debug_utc_used = None
+&nbsp; &nbsp;if Globals.is_not_None&#40;'use_utc'&#41;&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;seconds = time.gmtime&#40;timeinseconds&#41;&nbsp; 
## opposite direction&#58; seconds = calendar.timegm&#40;timeinseconds&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;debug_utc_used = 1
+&nbsp; &nbsp;else&#58;
+&nbsp; &nbsp;&nbsp; &nbsp;seconds = time.localtime&#40;timeinseconds&#41;
+
+&nbsp; &nbsp;s = time.strftime&#40;time_formatstring, seconds&#41;
+#&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; Time.py&#58; time_string is '%s' 
&#40;UTC&#58; '%s'&#41;" % &#40;s, debug_utc_used&#41;, 8&#41;
+
&nbsp;&nbsp; &nbsp;return s + gettzd&#40;timeinseconds&#41;
&nbsp;
+
&nbsp;def stringtotime&#40;timestring&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Return time in seconds from w3 timestring
&nbsp;
@@ -72,10 +102,19 @@
&nbsp;&nbsp; &nbsp;like a w3 datetime string, return None.
&nbsp;
&nbsp;&nbsp; &nbsp;"""
+
+## OM&#58; AIM&#58; The processing of separators should be fail-safe, even 
mixed separators
+## should be properly recognized and handled!
+
+&nbsp; &nbsp;regexp = re.compile&#40; '&#91;-&#58;_&#93;' &#41;&nbsp; ## 
WAS&#58; "-"
+#&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; timestring to process is '%s'" % 
&#40;timestring&#41;, 9 &#41;
+
&nbsp;&nbsp; &nbsp;try&#58;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;date, daytime = 
timestring&#91;&#58;19&#93;.split&#40;"T"&#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;year, month, day = map&#40;int, 
date.split&#40;"-"&#41;&#41;
-&nbsp; &nbsp;&nbsp; &nbsp;hour, minute, second = map&#40;int, 
daytime.split&#40;"&#58;"&#41;&#41;
+
+## OM&#58; re.split&#40;&#41; is required, using 'string'.split&#40;&#41; is 
NOT possible with regexp-char group.
+&nbsp; &nbsp;&nbsp; &nbsp;hour, minute, second = map&#40;int, 
re.split&#40;regexp, daytime&#41; &#41;
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert 1900 < year < 2100, year
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert 1 <= month <= 12
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;assert 1 <= day <= 31
@@ -144,11 +183,16 @@
&nbsp;&nbsp; &nbsp;"""Return w3's timezone identification string.
&nbsp;
&nbsp;&nbsp; &nbsp;Expresed as &#91;+/-&#93;hh&#58;mm.&nbsp; For instance, PDT 
is -07&#58;00 during
-&nbsp; &nbsp;dayling savings and -08&#58;00 otherwise.&nbsp; Zone is coincides 
with what
+&nbsp; &nbsp;dayling savings and -08&#58;00 otherwise.&nbsp; Zone coincides 
with what
&nbsp;&nbsp; &nbsp;localtime&#40;&#41;, etc., use.&nbsp; If no argument given, 
use the current
&nbsp;&nbsp; &nbsp;time.
&nbsp;
&nbsp;&nbsp; &nbsp;"""
+
+&nbsp; &nbsp;if Globals.is_not_None&#40;'use_utc'&#41;&#58;
+#&nbsp; &nbsp;&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; Globals&#40;use_utc&#41; is 
set, instantly returning 'Z' &#40;'Zulu' UTC identifier&#41;.", 7&#41;
+&nbsp; &nbsp;&nbsp; &nbsp;return 'Z'
+
&nbsp;&nbsp; &nbsp;if timeinseconds is None&#58; timeinseconds = 
time.time&#40;&#41;
&nbsp;&nbsp; &nbsp;dst_in_effect = time.daylight and 
time.localtime&#40;timeinseconds&#41;&#91;8&#93;
&nbsp;&nbsp; &nbsp;if dst_in_effect&#58; offset = -time.altzone/60
@@ -157,16 +201,21 @@
&nbsp;&nbsp; &nbsp;elif offset < 0&#58; prefix = "-"
&nbsp;&nbsp; &nbsp;else&#58; return "Z" # time is already in UTC
&nbsp;
+&nbsp; &nbsp;if Globals.is_not_None&#40;'use_compatible_timestamps'&#41;&#58; 
time_separator = '-'
+&nbsp; &nbsp;else&#58; time_separator = '&#58;'
+&nbsp; &nbsp;log.Log&#40;"DEBUG&#58; timezone_format separator used&#58; 
'%s'.\n" % time_separator, 8&#41;
+
&nbsp;&nbsp; &nbsp;hours, minutes = map&#40;abs, divmod&#40;offset, 60&#41;&#41;
&nbsp;&nbsp; &nbsp;assert 0 <= hours <= 23
&nbsp;&nbsp; &nbsp;assert 0 <= minutes <= 59
-&nbsp; &nbsp;return "%s%02d&#58;%02d" % &#40;prefix, hours, minutes&#41;
+&nbsp; &nbsp;return "%s%02d%s%02d" % &#40;prefix, hours, time_separator, 
minutes&#41;
&nbsp;
&nbsp;def tzdtoseconds&#40;tzd&#41;&#58;
&nbsp;&nbsp; &nbsp;"""Given w3 compliant TZD, return how far ahead UTC is"""
+&nbsp; &nbsp;"""Now accepts both '&#58;' and '-' separators"""
&nbsp;&nbsp; &nbsp;if tzd == "Z"&#58; return 0
&nbsp;&nbsp; &nbsp;assert len&#40;tzd&#41; == 6 # only accept forms like 
+08&#58;00 for now
-&nbsp; &nbsp;assert &#40;tzd&#91;0&#93; == "-" or tzd&#91;0&#93; == "+"&#41; 
and tzd&#91;3&#93; == "&#58;"
+&nbsp; &nbsp;assert &#40;tzd&#91;0&#93; == "-" or tzd&#91;0&#93; == "+"&#41; 
and &#40; tzd&#91;3&#93; == "&#58;" or tzd&#91;3&#93; == "-" or tzd&#91;3&#93; 
== "_" &#41;
&nbsp;&nbsp; &nbsp;return -60 * &#40;60 * int&#40;tzd&#91;&#58;3&#93;&#41; + 
int&#40;tzd&#91;4&#58;&#93;&#41;&#41;
&nbsp;
&nbsp;def cmp&#40;time1, time2&#41;&#58;
@@ -239,6 +288,8 @@
&nbsp;&nbsp; &nbsp;match = _genstr_date_regexp1.search&#40;timestr&#41; or \
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; 
&nbsp;_genstr_date_regexp2.search&#40;timestr&#41;
&nbsp;&nbsp; &nbsp;if not match&#58; error&#40;&#41;
+&nbsp; &nbsp;
+&nbsp; &nbsp;## OM&#58; No need to change below format from '&#58;', because 
not used for filenames and stringtotime&#40;&#41; was made failsafe.
&nbsp;&nbsp; &nbsp;timestr = "%s-%02d-%02dT00&#58;00&#58;00%s" % 
&#40;match.group&#40;'year'&#41;,
&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; 
&nbsp;int&#40;match.group&#40;'month'&#41;&#41;, 
int&#40;match.group&#40;'day'&#41;&#41;, gettzd&#40;&#41;&#41;
&nbsp;&nbsp; &nbsp;t = stringtotime&#40;timestr&#41;



Cheers,
Oliver Mulatz

+----------------------------------------------------------------------
|This was sent by address@hidden via Backup Central.
|Forward SPAM to address@hidden
+----------------------------------------------------------------------






reply via email to

[Prev in Thread] Current Thread [Next in Thread]