[Pythonmac-SIG] cheol 0.5.1 and chtab 0.1

Gordon Worley redbird@rbisland.cx
Fri, 20 Jul 2001 22:13:07 -0400


Okay, below is the diff file between cheol 0.5 and 0.5.1 so you can 
patch the source I sent out earlier.  Basically, it just adds support 
for long options (like --help) after reading up on getopt a little 
more.  Also, chtab is out.  It's basically cheol with the important 
parts changed to convert tabs/spaces instead of line endings.

Also, sorry about the largeness of these posts.  I'll just do diff 
files from now on and sometime next week I'll have both these scripts 
posted on my Web site.  Actually, now that I think about it, chtab 
could probably be a diff on cheol.  :-)  Oh well.

If anyone has feature ideas, please, send them my way (but keep in 
mind these are Unixy utility programs, so don't ask for them to clean 
the kitchen sink).  I think there may be a feature or two that could 
be added to chtab, but I'm not sure what, since I got it to do the 
conversions I would want it to do.

cheol0.5-0.5.1.diff:

22a23
>  0.5.1 - added support for long (--) options
31c32
< __version__ = "cheol 0.5, Copyright 2000-2001 Gordon Worley via the 
Python License, version 2.1.\nType -h for help"
---
>  __version__ = "cheol 0.5.1, Copyright 2000-2001 Gordon Worley via 
>the Python License, version 2.1.\nType -h for help"
195,198c196,199
<    -m : macify line endings
<    -u : unixify line endings
<    -d : dosify line endings
<    -h : print help
---
>     -m, --mac : macify line endings
>     -u, --unix : unixify line endings
>     -d, --dos : dosify line endings
>     -h, --help : print help
199a201,202
>
>  run with no options or args for version string
205c208
< 		opts, args = getopt.getopt(sys.argv[1:], "hlvmudr")
---
>  		opts, args = getopt.getopt(sys.argv[1:], "hlvmudr", 
>("help", "mac", "unix", "dos"))
220c223
< 		elif opt[0] == "-m":
---
>  		elif opt[0] == "-m" or opt[0] == "--mac":
222c225
< 		elif opt[0] == "-u":
---
>  		elif opt[0] == "-u" or opt[0] == "--unix":
224c227
< 		elif opt[0] == "-d":
---
>  		elif opt[0] == "-d" or opt[0] == "--dos":
226c229
< 		elif opt[0] == "-h":
---
>  		elif opt[0] == "-h" or opt[0] == "--help":
232c235
< 		convert(arg, mode, is_recv, follow_links, verbose)
\ No newline at end of file
---
>  		convert(arg, mode, is_recv, follow_links, verbose)


chtab 0.1:

#!/usr/bin/env python

"""
chtab:  tabs to spaces and spaces to tabs
Copyright (C) 2000-2001 Gordon Worley

This program is free software; you can redistribute it and/or modify
it under the terms of the Python License, version 2.1.

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
Python License, version 2.1 for more details.

For a copy of the Python License, version 2.1, visit
<http://www.python.org/>.

To contact me, please visit my Web site at <http://homepage.mac.com/
redbird/> or e-mail me at <redbird@rbisland.cx>.

#history

0.1 - fixed regexen
0.0 - based on cheol.py, but puts in regexen to convert tabs/spaces
"""

__version__ = "chtab 0.1, Copyright 2000-2001 Gordon Worley via the 
Python License, version 2.1.\nType -h for help"
#fn = filename

#this first part is not by me, but put right in this code
#so that everything can stay in one file :-)
#by Jurgen Hermann, from Python Cookbook (ASPN)

import os, string

def replaceFile(oldname, newname):
	""" Rename file 'oldname' to 'newname'.
	"""
	if os.name == 'nt' and os.path.exists(oldname):
		# POSIX rename does an atomic replace, WIN32 rename 
does not. :-(
		try:
			os.remove(newname)
		except OSError, exc:
			import errno
			if exc.errno != errno.ENOENT: raise exc

	# rename it
	os.rename(oldname, newname)


class FileMorpher:
	""" A class that enables a client to securely update an existing file,
		including the ability to make an automated backup version.
	"""

	def __init__(self, filename, **kw):
		""" The constructor takes the filename and some options.

			backup -- boolean indicating whether you want 
a backup file

				(default is yes)
		"""
		self.filename = filename
		self.do_backup = kw.get('backup', 0)

		self.stream = None
		self.basename, ext = os.path.splitext(self.filename)


	def __del__(self):
		if self.stream:
			# Remove open temp file
			self.__close()
			os.remove(self.__tempfile())


	def __tempfile(self):
		return self.basename + ".tmp"


	def __close(self):
		""" Close temp stream, if open.
		"""
		if self.stream:
			self.stream.close()
			self.stream = None


	def load(self):
		""" Load the content of the original file into a string and
			return it. All I/O exceptions are passed through.
		"""
		file = open(self.filename, "rt")
		try:
			content = file.read()
		finally:
			file.close()

		return content


	def save(self, content):
		""" Save new content, using a temporary file.
		"""
		file = self.opentemp()
		file.write(content)
		self.commit()


	def opentemp(self):
		""" Open a temporary file for writing and return an 
open stream.
		"""
		assert not self.stream, "Write stream already open"

		self.stream = open(self.__tempfile(), "wt")

		return self.stream


	def commit(self):
		""" Close the open temp stream and replace the original file,
			optionally making a backup copy.
		"""
		assert self.stream, "Write stream not open"

		# close temp file
		self.__close()

		# do optional backup and rename temp file to the correct name
		if self.do_backup:
			replaceFile(self.filename, self.basename + ".bak")
		replaceFile(self.__tempfile(), self.filename)

#end part not by me
#begin part by me

import re

def convert(fn, mode, is_recv, follow_links, verbose, tab_width, all):
	if is_recv:
		if os.path.isdir(fn) and not os.path.islink(fn):
			if verbose:
				print "%s/:" % fn
			os.chdir(fn)
			fns = os.listdir("./")
			for afn in fns:
				convert(afn, mode, is_recv, 
follow_links, verbose, tab_width, all)
			os.chdir("..")
			if verbose:
				print "../:"
		elif os.path.isdir(fn) and os.path.islink(fn) and 
os.path.islink(fn) <= follow_links:
			jfn = os.readlink(fn)
			if verbose:
				print "%s/ (%s/):" % (fn, jfn)
			fns = os.listdir(fn)
			for afn in fns:
				convert(os.path.join(jfn, afn), mode, 
is_recv, follow_links, verbose, tab_width, all)
			if verbose:
				print "../:"
	if not os.path.isdir(fn):
		if os.path.islink(fn) and os.path.islink(fn) <= follow_links:
			tmp = fn
			fn = os.readlink(fn)
			if verbose:
				print "converting %s (%s)" % (tmp, fn)
		elif verbose:
			print "converting %s" % fn
		f = FileMorpher(fn)
		temp = f.load()
		if mode == 0:
			if all:
				temp = re.sub(r" {" + `tab_width` + 
r"}", r"\t", temp)
			else:
				patt = re.compile(r"^ {" + 
`tab_width` + r"}", re.M)
				temp, count = patt.subn(r"\t", temp)
				i = 1
				while count > 0:
					subpatt = re.compile(r"^\t{" 
+ `i` + r"} {" + `tab_width` + r"}", re.M)
					temp, count = 
subpatt.subn("\t"*(i+1), temp)
					i += 1
		elif mode == 1:
			temp = temp.expandtabs(tab_width)
		stream = f.opentemp()
		stream.write(temp)
		f.commit()

help = """\
chtab:  En/detabs files

%s [options] <path ...>
  options:
    -r : recursive
    -l : follow links
    -v : verbose
    -e, --entab : turn spaces into tabs (default)
    -d, --detab : turn tabs into spaces
    -t <num> : number of spaces per tab (default is 4)
    -a : act on all spaces (works with --entab, default is to just affect
		   those spaces at the beginning of lines)
    -h, --help : print help
  path is a file or directory

run with no options or args for version string
"""

if __name__ == '__main__':
	import sys, getopt
	try:
		opts, args = getopt.getopt(sys.argv[1:], "ahlvedrt:", 
("entab", "detab", "help"))
	except:
		print "That's not an option.  Type -h for help."
		sys.exit(1)
	mode = 0 #default is to entab
	all = 0 #default is to just do tabs at start of lines
	tab_width = 4
	is_recv = 0 #default isn't recursive
	follow_links = 0 #default don't follow links
	verbose = 0
	for opt in opts:
		if opt[0] == "-r":
			is_recv = 1
		elif opt[0] == "-a":
			all = 1
		elif opt[0] == "-l":
			follow_links = 1
		elif opt[0] == "-v":
			verbose = 1
		elif opt[0] == "-t":
			tab_width = int(opt[1])
		elif opt[0] == "-e" or opt[0] == "--entab":
			mode = 0;
		elif opt[0] == "-d" or opt[0] == "--detab":
			mode = 1;
		elif opt[0] == "-h" or opt[0] == "--help":
			print help % sys.argv[0]
			sys.exit(0)
	if not args:
		print __version__
	for arg in args:
		convert(arg, mode, is_recv, follow_links, verbose, 
tab_width, all)

-- 
Gordon Worley                     `When I use a word,' Humpty Dumpty
http://homepage.mac.com/redbird/   said, `it means just what I choose
redbird@rbisland.cx                it to mean--neither more nor less.'
PGP:  0xBBD3B003                                  --Lewis Carroll