[alt.sources.d] Icon program to reverse the order of lines.

goer@ellis.uchicago.edu (Richard L. Goerwitz) (02/16/91)

In article <*||&R~@warwick.ac.uk> alfie@cs.warwick.ac.uk (Nick Holloway) writes:
>The included script reversing the order of lines in a file.  Unlike
>'rev' which reverses the characters in each line, 'reverse' will
>reverse the order of the lines.  Think of it as reversal in the other
>dimension!  The usage is "reverse [ file ... ]"

Just for fun, I did this in Icon.  The difference here is that the Icon
program I just wrote replaces the original with the reversed file.  It's
easy enough to reverse its effects.  Just run it again on the same files.
The usage is also somewhat different:

    revlines [-maxbuf] files

where maxbuf is the biggest chunk to be kept in core memory, without being
written to a tempfile, and where files are the names of the files to be
reversed (there can be any number of them).

Oh.  This program will work under MS-DOS, too, with its different path
conventions (i.e. \ for /).  It was easy enough to add support for it,
so I did.

This is not an extensively tested program, and since it's a single file,
I didn't even bother to archive it.

-Richard (goer@sophist.uchicago.edu)


############################################################################
#
#	Name:	 revlines.icn
#
#	Title:	 reverse lines in file
#
#	Author:	 Richard L. Goerwitz
#
#	Version: 1.1
#
############################################################################
#
#  Revlines reverses the order of lines in one or more files, using
#  temporary files if the sorting requires keeping more than a user-
#  specifiable amount of text in memory.  Usage is
#
#      revlines [-maxsize] files
#
#  where maxsize represents the maximum file size to be sorted in core
#  memory without using temporary files, and where files representes
#  one or more filenames to be reversed.
#
#  If you want to reverse the effects of revlines, run it again on the
#  file(s) in question.  They will end up precisely as they started
#  out before the first run of revlines.
#
############################################################################
#
#  Links: none
#
#  Requires: UNIX or MS-DOS, co-expressions
#
############################################################################

global filename, get_tempname

procedure main(a)

    local usage, tempnames, max, intext, loc,
	lines, outtext, final_tempname

    usage := "usage:  revlines [-maxsize] files"
    # where maxsize refers to the maximum bytes to hold in memory
    # before using temp files (default 262,000)

    if match("-",a[1])
    then max := integer(pop(a)) | stop(usage)
    else max := 262001   # approximate

    max <:= 255
    *a = 0 & stop(usage)

    every filename := !a do {

        tempnames := []
        get_tempname := create |gimme_a_temp()

	if not (intext := open(filename)) then
	    write(&errout, "revlines:  can't open ",filename)
	else {
	    repeat {
		lines := []
		loc := where(intext) + max
		until where(intext) >= loc do
		    push(lines, read(intext)) | break
		if *lines = 0 then break
		else {
		    outtext := open(tempname := |@get_tempname, "w") |
			abort(tempnames)
		    push(tempnames, tempname)
		    every write(outtext, !lines)
		    close(outtext)
		}
	    }
	    close(intext)
	    if *tempnames = 0 then
		write(&errout,"revlines:  ", filename, " is empty!")
	    else {
		outtext := open(final_tempname := |@get_tempname, "w") |
		    abort(tempnames)
		every tempname := !tempnames do {
		    if not (intext := open(tempname)) then {
			close(outtext)
			abort(tempnames ||| [final_tempname])
		    }
		    every write(outtext, !intext)
		    close(intext)
		}
		close(outtext)
		every remove(!tempnames|filename)
		if not rename(final_tempname, filename) then {
		    remove(final_tempname)
		    stop("revlines:  can't replace ", filename,
			 " with tempfile ",final_tempname)
		}
	    }
	}
    }
	    
end



procedure gimme_a_temp()

    local tempf, temp_name
    static slash, nonos, replc
    initial {
	if find("MS-DOS", &features)
	then slash := "\\"
	else if find("UNIX", &features)
	     then slash := "/"
	     else stop("revlines:  unsupported operating system")
	nonos := ~(&letters ++ &digits)
	replc := left(~nonos,*nonos,"Z")
    }

    filename ? {
	tempf := ""
	while tempf ||:= tab(find(slash)+1)
	tempf := getenv("TEMPDIR")
	tempf ||:= map(tab(0)[-8:0] | right(tab(0),8,"X"), nonos, replc)
    }

    every temp_name := tempf || "." || right(1 to 999,3,"0") do {
	close(open(temp_name)) & next
	suspend \temp_name
    }

end



procedure abort(l)
    write(&errout, "revlines:  can't open temp file; aborting")
    every remove(!l)
    exit(2)
end