Windows Scripting in Allegro CL

Now it is possible to write a small Lisp program callable from the command line on Windows. Allegro CL 8.0 and a patch are required to use the facility described here. Scripting on UNIX is described here.

To illustrate this feature, we present an example. This script is used to rename files which are the result of scanning photos. After scanning, the files are given descriptive names like cat-on-roof.jpg. After a large number of files are scanned, they are sorted into directories whose names contain the dates, sometimes approximate, when the pictures were taken. After the sorting, the task is to rename the files, again, so that their names contain the date. In other words:

  • batch scan a set of photos
  • first renaming (e.g., "cat-on-roof.jpg")
  • sorting of the pictures into directories based on day or event
  • second renaming (e.g., "1998-02-cat-on-roof.jpg")
The first renaming is unavoidable. The second renaming could be very time consuming. Not only that, if later any further refinements are made in the dates the pictures were taken, that would result in yet another round of renaming. This is what computers are for, to save us work, so let's write a program to do it.

The details of the program, scanprefix.cl (view or download), are not important. What is important is how we package it for use. We will use a new Allegro CL function, excl:lisp-to-bat-file , to create a .bat file from a .cl file. This .bat file can then be run from the Windows command shell (cmd.exe):

(excl:lisp-to-bat-file
  "scanprefix.cl"          ; source file
  "scanprefix.bat"         ; output file
  :executable "sys:build"  ; Lisp executable to use (more below)
  :image "sys:mlisp8.dxl"
  :pause nil               ; whether to pause at end (more below)
  :if-exists :supersede    ; whether to supersede the output file
  )

scanprefix.bat looks like this:

@echo off
"c:\Program Files\acl80\build" -I "c:\Program Files\acl80\mlisp8.dxl" --bat "c:\bin\scanprefix.bat" -- %*
goto xxx_l2b_end
;;NEED THIS COMMENT TO DELINEATE THE LISP CODE
...contents of scanprefix.cl removed for brevity...
:end-of-script
:xxx_l2b_end

In this case, a console app was chosen, instead of a Windows app. The difference is the output a console generates goes to the cmd.exe window, and the output from a Windows program goes into an application-specific window. See the Table of compatibible predefined images for more information. For example, from the table, the combination of build.exe and mlisp.dxl is not allowed. Not allowed means this command would fail:

c:\>"c:\Program Files\acl80\build.exe" -I mlisp.dxl

Error: The lisp executable and image file differ
as to the use of the international character set

Could not restore the image file:
    c:\Program Files\acl80\mlisp.dxl.

c:\>

If we wanted to make a windows app, we could do it this way:

(excl:lisp-to-bat-file
  "scanprefix.cl"          ; source file
  "scanprefix.bat"         ; output file
  :executable "sys:mlisp"  ; Lisp executable to use
  :image "sys:mlisp.dxl"
  :pause t                 ; whether to pause at end
  :if-exists :supersede    ; whether to supersede the output file
  )
Notice that we changed not only the :executable and :image keyword values, but also changed the :pause value to t.

Now, that we have made scanprefix.bat, we just need to put it in some directory in our PATH environment variable. Otherwise, we will need to fully qualify the path to the script. We can use it like this:

c:\>scanprefix

Usage: scanprefix [-x] directory
  -x        - do the renames, otherwise just print what would be done
  directory - the directory to scan for .jpg's
c:\> 
or
c:\>scanprefix c:/pictures/_TMP/scans
c:\pictures\_TMP\scans\1995-01-25\1994-12-24-vu-xmas\:
   1995-01-25-anh-parents.jpg => 1994-12-24-anh-parents.jpg
   1995-01-25-anh-parents2.jpg => 1994-12-24-anh-parents2.jpg
   1995-01-25-anh.jpg => 1994-12-24-anh.jpg
   1995-01-25-linda.jpg => 1994-12-24-linda.jpg
   1995-01-25-linda2.jpg => 1994-12-24-linda2.jpg
   1995-01-25-manh.jpg => 1994-12-24-manh.jpg
   1995-01-25-ngan.jpg => 1994-12-24-ngan.jpg
   anh-parents3.jpg => 1994-12-24-anh-parents3.jpg
   blind-wrap-done.jpg => 1994-12-24-blind-wrap-done.jpg
   blind-wrap.jpg => 1994-12-24-blind-wrap.jpg
   linda3.jpg => 1994-12-24-linda3.jpg
   manh.jpg => 1994-12-24-manh.jpg
   mickey-ngan2.jpg => 1994-12-24-mickey-ngan2.jpg
   mickey-ngan3.jpg => 1994-12-24-mickey-ngan3.jpg
c:\pictures\_TMP\scans\1995-01-25\1994-franz\:
   ethan.jpg => 1994-ethan.jpg
   hackysack.jpg => 1994-hackysack.jpg
   sara.jpg => 1994-sara.jpg
c:\pictures\_TMP\scans\1995-01-25\1995-misc\:
   alan.jpg => 1995-alan.jpg
   anh-alan.jpg => 1995-anh-alan.jpg
   anh3.jpg => 1995-anh3.jpg
   kevin-alan.jpg => 1995-kevin-alan.jpg

You can rename the above files by executing:
scanprefix -x c:/pictures/_TMP/scans

c:\>scanprefix -x c:/pictures/_TMP/scans
c:\pictures\_TMP\scans\1995-01-25\1994-12-24-vu-xmas\:
   renamed: 1995-01-25-anh-parents.jpg => 1994-12-24-anh-parents.jpg
   renamed: 1995-01-25-anh-parents2.jpg => 1994-12-24-anh-parents2.jpg
   renamed: 1995-01-25-anh.jpg => 1994-12-24-anh.jpg
   renamed: 1995-01-25-linda.jpg => 1994-12-24-linda.jpg
   renamed: 1995-01-25-linda2.jpg => 1994-12-24-linda2.jpg
   renamed: 1995-01-25-manh.jpg => 1994-12-24-manh.jpg
   renamed: 1995-01-25-ngan.jpg => 1994-12-24-ngan.jpg
   renamed: anh-parents3.jpg => 1994-12-24-anh-parents3.jpg
   renamed: anh.jpg => 1994-12-24-anh.jpg
   renamed: blind-wrap-done.jpg => 1994-12-24-blind-wrap-done.jpg
   renamed: blind-wrap.jpg => 1994-12-24-blind-wrap.jpg
   renamed: linda3.jpg => 1994-12-24-linda3.jpg
   renamed: manh.jpg => 1994-12-24-manh.jpg
   renamed: mickey-ngan2.jpg => 1994-12-24-mickey-ngan2.jpg
   renamed: mickey-ngan3.jpg => 1994-12-24-mickey-ngan3.jpg
c:\pictures\_TMP\scans\1995-01-25\1995-misc\:
   renamed: alan.jpg => 1995-alan.jpg
   renamed: anh-alan.jpg => 1995-anh-alan.jpg
   renamed: anh3.jpg => 1995-anh3.jpg
   renamed: kevin-alan.jpg => 1995-kevin-alan.jpg

Renamed 19 files.

c:\>

Another example using the -s argument to the script, also calling the script from BASH (you must use the full name of the script, including the .bat suffix):

bash$ scanprefix.bat c:/pictures/_TMP/scans

c:\pictures\_TMP\scans\1987-08-kentucky\:
  anh.jpg => 1987-08-anh.jpg
  anh-doris.jpg => 1987-08-anh-doris.jpg
  anh2.jpg => 1987-08-anh2.jpg
  andy-doris2.jpg => 1987-08-andy-doris2.jpg
  dog.jpg => 1987-08-dog.jpg
  andy-doris.jpg => 1987-08-andy-doris.jpg
  horses.jpg => 1987-08-horses.jpg
  horses2.jpg => 1987-08-horses2.jpg
  anh-reservoir.jpg => 1987-08-anh-reservoir.jpg
  country.jpg => 1987-08-country.jpg
  anh-doris2.jpg => 1987-08-anh-doris2.jpg
  anh-reservoir2.jpg => 1987-08-anh-reservoir2.jpg
  castle.jpg => 1987-08-castle.jpg
  andy.jpg => 1987-08-andy.jpg
  anh-wink.jpg => 1987-08-anh-wink.jpg

You can rename the above files by executing:
scanprefix -x c:/pictures/_TMP/scans
bash$ scanprefix.bat -x c:/pictures/_TMP/scans

c:\pictures\_TMP\scans\1987-08-kentucky\:
  renamed: anh.jpg => 1987-08-anh.jpg
  renamed: anh-doris.jpg => 1987-08-anh-doris.jpg
  renamed: anh2.jpg => 1987-08-anh2.jpg
  renamed: andy-doris2.jpg => 1987-08-andy-doris2.jpg
  renamed: dog.jpg => 1987-08-dog.jpg
  renamed: andy-doris.jpg => 1987-08-andy-doris.jpg
  renamed: horses.jpg => 1987-08-horses.jpg
  renamed: horses2.jpg => 1987-08-horses2.jpg
  renamed: anh-reservoir.jpg => 1987-08-anh-reservoir.jpg
  renamed: country.jpg => 1987-08-country.jpg
  renamed: anh-doris2.jpg => 1987-08-anh-doris2.jpg
  renamed: anh-reservoir2.jpg => 1987-08-anh-reservoir2.jpg
  renamed: castle.jpg => 1987-08-castle.jpg
  renamed: andy.jpg => 1987-08-andy.jpg
  renamed: anh-wink.jpg => 1987-08-anh-wink.jpg

Renamed 15 files.

For those interested in automating the creation of the scripts from source files, below is a GNU make makefile that does the work. GNU make can be installed on Windows from cygwin.com. Put this in a file called makefile:

# Makefile to turn lisp files into bat files, so they can be run from a DOS
# prompt (ie, in a cmd.exe).
# Kevin Layer, Jan 2006.
#
# Requirements:
#  1. Allegro CL 8.0, with patch aclstart.001 and startup.001.
#  2. GNU make

acldir = c:/Program Files/acl80

scripts = scanprefix.bat

default: $(scripts)

%.bat: %.cl makefile
	@rm -f build.tmp
	echo '(setq excl::*break-on-warnings* t)' >> build.tmp
#### The compilation is both a validation and creates a .fasl file which
#### can be used directly.
	echo '(compile-file "$*.cl")' >> build.tmp
	echo '(excl:lisp-to-bat-file "$*.cl" "$*.bat" ' >> build.tmp
#### Choose one:
####    the first uses a Windows console app, where the output
####       goes to the cmd.exe window where the script is run
####    the second a typical Windows app where the output goes
####       into its own Window.  The `pause' is needed for this
####       case because the output of the script will not be seen
####       unless there is a pause.  Of course, if you don't want
####       to see the output of the script, you leave :pause as nil.
	echo '  :executable "sys:build" ' >> build.tmp
	echo '  :pause nil ' >> build.tmp
#	echo '  :executable "sys:mlisp8" ' >> build.tmp
#	echo '  :pause t ' >> build.tmp
####
	echo '  :image "sys:mlisp8.dxl" ' >> build.tmp
	echo '  :if-exists :supersede ' >> build.tmp
	echo ')' >> build.tmp
	echo '(exit 0)' >> build.tmp
	sh "$(acldir)/src/runlisp.sh" -f build.tmp mlisp
# remove if the previous command succeeded
	@rm -f runlisp.out
	@rm -f build.tmp

clean: FORCE
	rm -f $(scripts) build.tmp runlisp.out

FORCE:

All that is necessary to add a new script is to add it to the scripts line in the makefile and then to execute make in the directory that contains the makefile and the Lisp source files.

bash$ make
echo '(setq excl::*break-on-warnings* t)' >> build.tmp
echo '(compile-file "scanprefix.cl")' >> build.tmp
echo '(excl::lisp-to-bat-file "scanprefix.cl" "scanprefix.bat" ' >> build.tmp
echo '  :executable "sys:build" ' >> build.tmp
echo '  :pause nil ' >> build.tmp
echo '  :image "sys:mlisp8.dxl" ' >> build.tmp
echo '  :if-exists :supersede ' >> build.tmp
echo ')' >> build.tmp
echo '(exit 0)' >> build.tmp
sh "c:/Program Files/acl80/src/runlisp.sh" -f build.tmp mlisp
mlisp +M +B +cn +s build.tmp -q -batch -backtrace-on-error -d runlisp.out
[lisp output redirected to runlisp.out]
bash$ 

NOTE: Because of some fiddling with *features* in the script, you can also load the fasl file associated with the script without having it execute code based on the command line arguments to the Lisp you load it into. That means you could do something like this:

cl-user(1): :ld c:/bin/scanprefix.fasl
...
cl-user(2): (scan-root "c:/pictures/_TMP/scans/")
...
Copyright © 2023 Franz Inc., All Rights Reserved | Privacy Statement Twitter