#! /usr/bin/env python
# -*- coding: utf-8 -*-

######################################################
#
# snapshot - take a snapshot of a given directory, and archive it compressed.
#          - toma una instantánea del directorio dado, y la guarda comprimida.
#
#   Copyright © 2003,2004,2005,2006,2007,2008,2009,2010 Alberto González Palomo
#   Author: Alberto González Palomo   <http://www.matracas.org>
#
#   This program 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 program 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.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
######################################################

import sys
import os
import getopt
import time
import re

def display_help():
	print '''
Usage: snapshot [options] [directory]...

  Package the directory contents to a "tar" file compressed with bzip2 or
gzip, and named as "directory_yyyymmdd_hhmm.tbz2" (.tgz for gzip), where
"directory" is the directory name given, "yyyymmdd" is the current date,
and "hhmm" is the current time.
  The archive file permissions are set to 440 (not writable) to make
accidental deletion a bit more difficult.
  The directory name gets striped in the tar file so that when untarring,
the result will be just the last part of the directory path: e.g., if you
ask to take a snapshot of directory "foo/bar/baz", the tar file will
have the contents of "baz", and extract them to a directory named "baz",
without "foo/bar". This is done to properly manage requests such as
"../../foo", wich will produce a snapshot of "foo" that extracts to
"foo/" in the current directory.
  If no directory is given, the snapshot gets the current one: e.g., if
you are in "/home/foo/projects/bar/", and type just "snapshot", you will
get a snapshot that extracts to "bar/", and is named "bar_yyyymmdd_hhmm.tbz2".

  -h, --help                    Display this message.
  -l, --label                   Extra label to add to the archive file name.
  -g, --gzip                    Use gzip instead of bzip2.
  -t, --just-test               Do not actually execute tar.
  -x, --exclude=pattern         Files to exclude, separated by commas.
                                For instance, to exclude all .svn and .cvs
                               directories and all C object files:
                                --exclude=.svn,.cvs,*.o
  -d, --snapshot-path=directory Snapshots directory. Default is "~/snapshots".
                                This can also be set with the environment
                               variable "SNAPSHOTS_PATH".
                               If this directory does not exist, it will be
                               created.

Report bugs to matmota@matracas.org
'''

def main():
	try: snapshots_path = os.environ['SNAPSHOTS_PATH']
	except: snapshots_path = '~/snapshots'
	label = ""
	tar_command = "tar jcvf"
	extension = "tbz2"
	just_test = 0
	exclude_pattern = ""
	try:
		opts, args = getopt.getopt(sys.argv[1:],
					   'hd:l:gtx:',
					   ('help',
					    'snapshots-dir=',
					    'label=',
					    'gzip',
					    'just-test',
					    'exclude='))
	except getopt.error, option:
		print 'Command line option problem: ', option, '\n'
		display_help()
		return(1)
	
	for o, a in opts:
		if (o == '-d')|(o == '--snapshots-dir'): snapshots_path = a
		if (o == '-l')|(o == '--label'):         label          = a
		if (o == '-g')|(o == '--gzip'):
			tar_command = "tar zcvf"
			extension = "tgz"
		if (o == '-t')|(o == '--just-test'):     just_test = 1
		if (o == '-x')|(o == '--exclude'):       exclude_pattern = a
		if (o == '-h')|(o == '--help'):
			display_help()
			return(0)

	if not args:
		args = ['.']
	if label:
		label = re.sub(r'[ \t\n();:,./\\]', r'_', label)
	snapshots_path = os.path.expanduser(snapshots_path)
	try: os.stat(snapshots_path)
	except os.error, problem:
		if (str(problem)[:9] == "[Errno 2]"):
			print 'Making new directory:', snapshots_path
			if not just_test: os.makedirs(snapshots_path)
	for directory in args:
		while (directory[-1:] == "/"): directory = directory[:-1]
		directory = os.path.abspath(directory)
		date = time.strftime("_%Y%m%d_%H%M",
				     time.localtime(time.time()))
		snapshot_base_name = os.path.abspath(directory)
		snapshot_base_name = os.path.basename(snapshot_base_name)
		snapshot_name = os.path.abspath(snapshots_path + '/'
						+ snapshot_base_name)
		if label:
			snapshot_name = snapshot_name + date + '.' + label + '.' + extension
		else:
			snapshot_name = snapshot_name + date + '.' + extension
		directory_tail = os.path.basename(directory)
		directory_head = os.path.dirname(directory)
		command = tar_command + ' "' + snapshot_name + '"'
		if exclude_pattern:
			for pattern in exclude_pattern.split(","):
				command += " --exclude '" + pattern + "'"
		if (directory_head != ""):
			command = ('cd "' + directory_head
				   + '"; ' + command)
		command = command + ' "' + directory_tail
		command = command + '"; chmod 440 "' + snapshot_name + '"'
		print command
		if not just_test:
			os.system(command)
			size = os.path.getsize(snapshot_name)
			if size > 1000000000:
				factor = 1000000000.0
				unit_name = "GB"
			elif size > 1000000:
				factor = 1000000.0
				unit_name = "MB"
			elif size > 1000:
				factor = 1000.0
				unit_name = "KB"
			else:
				factor = 1
				unit_name = "B"
			size_string = str(size/factor) + unit_name
			print "Stored", "%.2f"%(size/factor), unit_name, "in:\n", snapshot_name

main()
