summaryrefslogtreecommitdiff
path: root/ipl/progs/mszip.icn
diff options
context:
space:
mode:
Diffstat (limited to 'ipl/progs/mszip.icn')
-rw-r--r--ipl/progs/mszip.icn361
1 files changed, 361 insertions, 0 deletions
diff --git a/ipl/progs/mszip.icn b/ipl/progs/mszip.icn
new file mode 100644
index 0000000..2e6744a
--- /dev/null
+++ b/ipl/progs/mszip.icn
@@ -0,0 +1,361 @@
+############################################################################
+#
+# File: mszip.icn
+#
+# Subject: Program to ZIP a directory for MS-DOS use
+#
+# Author: Gregg M. Townsend
+#
+# Date: June 23, 2000
+#
+############################################################################
+#
+# This file is in the public domain.
+#
+############################################################################
+#
+# usage: mszip [options] root-directory zip-file
+# -n no action: just report; zip-file may be omitted
+# -v verbose commentary: list individual file types
+# -i check filenames for ISO 9660 (CD-ROM) legality
+#
+# Mszip stuffs the contents of a directory into a ZIP archive file,
+# translating text files to CRLF form. Pipes are opened that
+# require find, sort, and zip in the search path.
+#
+# The default report gives an inventory of files by extension. This
+# can be useful even without creating a ZIP file ("mszip -n dir").
+#
+# File types on the verbose report are:
+# x unreadable file
+# e empty file
+# b binary file
+# c text file with CRLFs
+# n text file with newlines
+# A file is "binary" if it contains more than 1% unexpected characters.
+#
+# Symlinks, FIFOs, device files etc. are reported and not archived.
+# Files with illegal MS-DOS names are reported but still archived.
+#
+############################################################################
+#
+# Requires: UNIX, zip program
+#
+############################################################################
+
+
+
+$define USAGE "[-n] [-v] [-i] root-directory zip-file"
+
+$define BTHRESH 0.01 # allowed fraction of wild bytes in text file
+
+$define BUFSIZ 65536 # size of buffer for checking binary/text
+ # (bytes beyond this many are not checked)
+
+$define ZIPOPTS "-9 -X" # best compression; omit uid/gid
+
+
+link options
+
+
+
+global verbose
+global errorcount
+global allfiles, binlist, txtlist
+global extns
+
+
+
+procedure main(args)
+ local opts, root, zipopts, zipname
+ local pwd, pipe, fname, errmsg
+ local nmproc
+
+ # process options
+ opts := options(args, "nvi")
+ verbose := opts["v"]
+ if \opts["i"] then
+ nmproc := isoname
+ else
+ nmproc := msname
+
+ root := args[1] | stop("usage: ", &progname, " ", USAGE)
+
+ # get current directory name and prepend to zip file if necessary
+ if /opts["n"] then {
+ zipname := args[2] | stop("usage: ", &progname, USAGE)
+ pipe := popen("pwd")
+ pwd := read(pipe) | stop("can't read current directory")
+ close(pipe)
+ if not zipname ? ="/" then
+ zipname := pwd || "/" || zipname
+ }
+
+ # change to source directory
+ chdir(root) | stop("can't change to directory: ", root)
+
+ # verify that zip file is writable
+ if \zipname then {
+ if not close(open(zipname, "w")) then
+ stop(zipname, ": cannot write")
+ remove(zipname)
+ }
+
+ # initialize
+ errorcount := 0
+ extns := table("")
+ allfiles := []
+ binlist := []
+ txtlist := []
+
+ # check for "bad" files: symlinks, fifos, etc.
+ write(&errout, "finding files...")
+ pipe := popen("find . ! -type d ! -type f -print | sort")
+ while report(read(pipe), "bad file type")
+ close(pipe)
+
+ # get list of the rest
+ pipe := popen("find . -type f -print | sort")
+ while fname := read(pipe) do {
+ put(allfiles, fname)
+ if not nmproc(fname) then
+ report(fname, "illegal filename")
+ }
+ close(pipe)
+
+ # inspect files
+ write(&errout, "inspecting files...")
+ while inspect(get(allfiles))
+
+ # summarize file types by extension
+ summary()
+
+ # write zip file, if -n was not specified
+ if \zipname then {
+
+ zipopts := ZIPOPTS
+ if /verbose then
+ zipopts := ZIPOPTS || " -q"
+
+ # create zip file and fill with text files
+ write(&errout, "storing text files...")
+ pipe := popen("zip -l " || zipopts || " " || zipname || " -@", "w")
+ every write(pipe, !txtlist)
+ close(pipe)
+
+ # add binary files to zip file
+ write()
+ write(&errout, "storing binary files...")
+ pipe := popen("zip -g " || zipopts || " " || zipname || " -@", "w")
+ every write(pipe, !binlist)
+ close(pipe)
+ }
+
+ # exit
+ if errorcount > 0 then
+ stop("\t", errorcount, " error(s)")
+ else
+ write("done.")
+end
+
+
+
+# popen(cmd, mode) -- open pipe, and abort on error
+
+procedure popen(cmd, mode)
+ local f
+
+ mode := "p" || (\mode | "r")
+ f := open(cmd, mode) | stop("can't open pipe: ", cmd)
+ return f
+end
+
+
+
+# census(s, c, lim) -- count occurrences of members of c in string s
+#
+# If lim is given, counting can stop early.
+
+procedure census(s, c, lim)
+ local n
+
+ /lim := *s
+ n := 0
+ s ? {
+ while n < lim & tab(upto(c)) do
+ n +:= *tab(many(c))
+ }
+ n >:= lim
+ return n
+end
+
+
+
+# msname(fname) -- check filename for MS-DOS legality
+
+procedure msname(fname)
+ local dir, base, ext
+ static forbid
+ initial forbid := &cset -- &letters -- &digits -- '/._^$~!#%&-{}()@\'`'
+
+ fname ? {
+ if upto(forbid) then fail # forbidden char
+ while dir := tab(upto('/') + 1) do
+ if *dir > 9 then fail # dir component too long
+ if base := tab(upto('.')) then {
+ move(1)
+ if upto('.') then fail # two periods
+ ext := tab(0)
+ }
+ else {
+ base := tab(0)
+ ext := ""
+ }
+ if (*base > 8) | (*ext > 3) then fail # component too long
+ }
+ return
+end
+
+
+
+# isoname(fname) -- check for ISO-9660 (CD-ROM) filename legality
+#
+# (disallows explicit version numbers)
+
+procedure isoname(fname)
+ static legal
+ initial legal := &lcase ++ &ucase ++ &digits ++ '_.'
+
+ fname ? {
+ while tab(upto('/') + 1)
+ tab(many(legal))
+ if pos(0) then
+ return msname(fname)
+ else
+ fail
+ }
+end
+
+
+
+# inspect(fname) -- inspect one file and update lists
+
+procedure inspect(fname)
+ local c
+
+ fname ? {
+ if ="./" then
+ fname := tab(0)
+ }
+
+ c := ftype(fname)
+ count(fname, c)
+ if \verbose then write(c, " ", fname)
+
+ if c == "x" then {
+ report(fname, "unreadable file")
+ return
+ }
+
+ if c == "n" then
+ put(txtlist, fname)
+ else
+ put(binlist, fname)
+
+ return
+end
+
+
+
+# ftype(fname) -- return file type character
+
+procedure ftype(fname)
+ local f, s, lim
+ static bset
+ initial bset := # allows \a\b\t\n\v\f\r\^Z
+ '\0\1\2\3\4\5\6\16\17\20\21\22\23\24\25\26\27\30\31\33\34\35\36\37' ++
+ &cset[128+:33]
+
+ f := open(fname, "ru") | return "x"
+ s := reads(f, BUFSIZ)
+ close(f)
+
+ if /s | (*s = 0) then return "e"
+ lim := BTHRESH * *s
+ if census(s, bset, lim) >= lim then return "b"
+ else if census(s, '\l') > census(s, '\r') then return "n"
+ else return "c"
+end
+
+
+
+# count(fname, typechar) -- count file extension
+
+procedure count(fname, tchar)
+ local extn
+
+ fname ? {
+ while tab(upto('/') + 1)
+ if tab(upto('.') + 1) then {
+ while tab(upto('.') + 1)
+ extn := tab(0)
+ }
+ else
+ extn := ""
+ }
+ extns[extn] ||:= tchar
+ return
+end
+
+
+
+# report(fname, errmsg) -- report error
+
+procedure report(fname, errmsg)
+ write(&errout, "\t", errmsg, ": ", fname)
+ errorcount +:= 1
+ return
+end
+
+
+
+# summary() -- generate summary of extension counts
+
+procedure summary()
+ local tlist, ext, s, b, c, e, n, x, tb, tc, te, tn, tx
+
+ write()
+ write(" unrd empty bin crlf newln extension")
+ tb := tc := te := tn := tx := 0
+
+ tlist := sort(extns, 3)
+ while ext := get(tlist) do {
+ s := get(tlist)
+ tb +:= (b := census(s, 'b'))
+ tc +:= (c := census(s, 'c'))
+ te +:= (e := census(s, 'e'))
+ tn +:= (n := census(s, 'n'))
+ tx +:= (x := census(s, 'x'))
+ write(r5(x), r5(e), r5(b), r5(c), r5(n), " .", ext)
+ }
+
+ write()
+ write(r5(tx), r5(te), r5(tb), r5(tc), r5(tn), " TOTAL: ", tx+te+tb+tc+tn)
+ write()
+ return
+end
+
+
+
+# r5(n) -- show integer in 5-char field, if nonzero
+
+procedure r5(n)
+ local s
+
+ if n = 0 then return " "
+ s := integer(n)
+ if *s < 5 then
+ return right(s, 5)
+ else
+ return " " || s
+end