[Pythonmac-SIG] cheol (Content entry, sort of)

Gordon Worley redbird@rbisland.cx
Wed, 18 Jul 2001 21:18:44 -0400


Okay, I promised, and thus I deliver.  :-)

Below is my command line tool for changing the line ending characters 
in text files.  It supports regular expression pattern matching since 
I'm not sure how well regular shell pattern expansion would work when 
converted to work on Classic as well as OS X.  As you might have 
guessed, this is a command line tool (since I don't use MacPython 
anymore, just Python on OS X), so if you want an interface someone 
else is going to have to do it.  Also, I decided not to include 
en/detabbing, since this is a line ending changer and it wouldn't be 
very Unixy if I started adding all sorts of features willy nilly (in 
fact, I think I'm going to take out the pattern matching when I 
officially release a version of this on my Web site since it's just 
there to make it easy to add a Mac interface to).  Oh, also, someone 
might want to improve the converting to dos line endings, since it 
will screw up the file if you type 'cheol -d dos_file.txt' (but I 
rarely need to convert to dos line endings so I just don't worry 
about it).

Finally, some of the variable names suck, and I know it, but that's 
just me being a BOFH so that I will be assured to win all future 
contests that want to use this code.  ;-P

Oh, and as far as help, man page forthcoming.  ;^)

cheol.py:

#!/usr/bin/python

"""
cheol:  change end of line character
Copyright (C) 2000-2001 Gordon Worley

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 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.

For a copy of the GNU General Public License, visit
<http://www.gnu.org/> or write to the Free Software Foundation, Inc.,
59 Temple Place--Suite 330, Boston, MA 02111-1307, USA.

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

#history

#0.3 added help and ability to convert to mac and dos.  Name changed
  to cheol.py from unixify.pl.
#0.1 added recursion, uses Jurgen Hermann's FileMorpher
#0.0 just converts the given files
"""

__version__ = "cheol 0.3, Copyright Gordon Worley via the GNU 
GPL.\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 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, pattern, mode, is_recv):
	if is_recv:
		if os.path.isdir(fn) and not os.path.islink(fn):
			fns = os.listdir(fn)
			for afn in fns:
				convert(os.path.join(fn, afn), 
pattern, mode, is_recv)
	if re.match(pattern, fn) and not os.path.isdir(fn):
		f = FileMorpher(fn)
		temp = f.load()
		if mode == 1:
			temp = string.replace(temp, '\n\r', '\n')
			temp = string.replace(temp, '\r', '\n')
		elif mode == 0:
			temp = string.replace(temp, '\n\r', '\r')
			temp = string.replace(temp, '\n', '\r')
		elif mode == 2:
			#this code could be dangerous, but I don't do these
			#conversions often enough to care :-P
			temp = string.replace(temp, '\r', '\n')
			temp = string.replace(temp, '\n', '\n\r')
		stream = f.opentemp()
		stream.write(temp)
		f.commit()

help = """\
cheol:	Converts EOL characters

%s [options] <path ...>
  options
  -r : recursive
  -m : macify line endings
  -u : unixify line endings
  -d : dosify line endings
  -p <pattern> : apply only to filenames matching <pattern>
  -h : print help
  path is a file or directory
"""

if __name__ == '__main__':
	import sys, getopt
	opts, args = getopt.getopt(sys.argv[1:], "hmudrp:")
	mode = 1 #default is unix, 0 is mac, 2 is dos
	is_recv = 0 #is recursive
	pattern = ""
	for opt in opts:
		if opt[0] == "-r":
			is_recv = 1
		elif opt[0] == "-p":
			pattern = opt[1]
		elif opt[0] == "-m":
			mode = 0;
		elif opt[0] == "-u":
			mode = 1;
		elif opt[0] == "-d":
			mode = 2;
		elif opt[0] == "-h":
			print help % sys.argv[0]
			sys.exit(0)
	if not args:
		print __version__
	for arg in args:
		convert(arg, pattern, mode, is_recv);
-- 
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