[PATCH (dockapps)] ascd: Add version 0.13.2 to repository.

Doug Torrance <[email protected]>
Newsgroups gmane.compw.window-managers.windowmaker.devel
Message-ID <[email protected]>
Obtained from:
https://web.archive.org/web/20090620082249/http://worldserver.oleane.com/rsn/ascd-en.html
---
 ascd/Imakefile                         |    7 +
 ascd/README                            |   50 +
 ascd/ascd/Imakefile                    |   63 ++
 ascd/ascd/README                       |  138 +++
 ascd/ascd/ascd.c                       |  764 ++++++++++++++
 ascd/ascd/ascd.h                       |  186 ++++
 ascd/ascd/ascd.man                     |  267 +++++
 ascd/ascd/cdcontrol.c                  |  362 +++++++
 ascd/ascd/config.h                     |    6 +
 ascd/ascd/configure                    |  203 ++++
 ascd/ascd/dirs.h                       |    3 +
 ascd/ascd/doc/CHANGES                  |  597 +++++++++++
 ascd/ascd/doc/NEWS                     |  148 +++
 ascd/ascd/ext.h                        |  164 +++
 ascd/ascd/faktory.c                    | 1777 ++++++++++++++++++++++++++++++++
 ascd/ascd/faktory_prot.h               |   15 +
 ascd/ascd/handlers.c                   |  445 ++++++++
 ascd/ascd/misc.c                       |  431 ++++++++
 ascd/ascd/misc/config.h.default        |    5 +
 ascd/ascd/misc/default                 |    8 +
 ascd/ascd/misc/dirs.h.default          |    3 +
 ascd/ascd/mixer_ext.h                  |   13 +
 ascd/ascd/mixer_global.c               |   15 +
 ascd/ascd/mixer_handlers.c             |  134 +++
 ascd/ascd/themes/default.tar           |  Bin 0 -> 40960 bytes
 ascd/ascd/themes/themes-manual.ps.gz   |  Bin 0 -> 82472 bytes
 ascd/ascd/themes/themes.tar            |  Bin 0 -> 92160 bytes
 ascd/ascd/version.h                    |    1 +
 ascd/ascd/wings_ext.h                  |   75 ++
 ascd/ascd/wings_global.c               |   79 ++
 ascd/ascd/wings_main.c                 | 1070 +++++++++++++++++++
 ascd/ascd/workman/include              |    1 +
 ascd/ascd/workman/wm_cdda.h            |  117 +++
 ascd/ascd/workman/wm_cddb.h            |   42 +
 ascd/ascd/workman/wm_cdinfo.h          |   78 ++
 ascd/ascd/workman/wm_cdrom.h           |   64 ++
 ascd/ascd/workman/wm_config.h          |  357 +++++++
 ascd/ascd/workman/wm_database.h        |   44 +
 ascd/ascd/workman/wm_helpers.h         |  105 ++
 ascd/ascd/workman/wm_index.h           |   36 +
 ascd/ascd/workman/wm_platform.h        |   51 +
 ascd/ascd/workman/wm_scsi.h            |   48 +
 ascd/ascd/workman/wm_struct.h          |  181 ++++
 ascd/ascd/workman/wm_version.h         |   35 +
 ascd/ascd/workman/workman.h            |   51 +
 ascd/ascd/workman/workman_defs.h       |   30 +
 ascd/configure                         |    9 +
 ascd/default                           |    9 +
 ascd/libworkman/COPYING.LIB            |  481 +++++++++
 ascd/libworkman/Config                 |   48 +
 ascd/libworkman/Imakefile              |   74 ++
 ascd/libworkman/README                 |    6 +
 ascd/libworkman/buildindex.c           |  223 ++++
 ascd/libworkman/cddaslave.c            |  565 ++++++++++
 ascd/libworkman/cddb.c                 |  541 ++++++++++
 ascd/libworkman/cdinfo.c               |  887 ++++++++++++++++
 ascd/libworkman/cdrom.c                |  793 ++++++++++++++
 ascd/libworkman/database.c             | 1522 +++++++++++++++++++++++++++
 ascd/libworkman/drv_sony.c             |  173 ++++
 ascd/libworkman/drv_toshiba.c          |  154 +++
 ascd/libworkman/include/wm_cdda.h      |  117 +++
 ascd/libworkman/include/wm_cddb.h      |   42 +
 ascd/libworkman/include/wm_cdinfo.h    |   78 ++
 ascd/libworkman/include/wm_cdrom.h     |   64 ++
 ascd/libworkman/include/wm_config.h    |  357 +++++++
 ascd/libworkman/include/wm_database.h  |   44 +
 ascd/libworkman/include/wm_helpers.h   |  105 ++
 ascd/libworkman/include/wm_index.h     |   36 +
 ascd/libworkman/include/wm_platform.h  |   51 +
 ascd/libworkman/include/wm_scsi.h      |   48 +
 ascd/libworkman/include/wm_struct.h    |  181 ++++
 ascd/libworkman/include/wm_version.h   |   35 +
 ascd/libworkman/include/workman.h      |   51 +
 ascd/libworkman/include/workman_defs.h |   30 +
 ascd/libworkman/index.c                |  385 +++++++
 ascd/libworkman/plat_aix.c             |  443 ++++++++
 ascd/libworkman/plat_bsd386.c          |  500 +++++++++
 ascd/libworkman/plat_freebsd.c         |  565 ++++++++++
 ascd/libworkman/plat_hpux.c            |  355 +++++++
 ascd/libworkman/plat_irix.c            |  456 ++++++++
 ascd/libworkman/plat_linux.c           |  866 ++++++++++++++++
 ascd/libworkman/plat_linux_audio.c     |  490 +++++++++
 ascd/libworkman/plat_linux_cdda.c      |  371 +++++++
 ascd/libworkman/plat_news.c            |  474 +++++++++
 ascd/libworkman/plat_openbsd.c         |  530 ++++++++++
 ascd/libworkman/plat_osf1.c            |  666 ++++++++++++
 ascd/libworkman/plat_sun.c             | 1188 +++++++++++++++++++++
 ascd/libworkman/plat_sun_audio.c       |  489 +++++++++
 ascd/libworkman/plat_sun_cdda.c        |  371 +++++++
 ascd/libworkman/plat_svr4.c            |  458 ++++++++
 ascd/libworkman/plat_template.c        |  269 +++++
 ascd/libworkman/plat_ultrix.c          |  666 ++++++++++++
 ascd/libworkman/scsi.c                 |  531 ++++++++++
 ascd/libworkman/wm_helpers.c           |  229 ++++
 ascd/version.h                         |    1 +
 95 files changed, 25296 insertions(+)
 create mode 100644 ascd/Imakefile
 create mode 100644 ascd/README
 create mode 100644 ascd/ascd/Imakefile
 create mode 100644 ascd/ascd/README
 create mode 100644 ascd/ascd/ascd.c
 create mode 100644 ascd/ascd/ascd.h
 create mode 100644 ascd/ascd/ascd.man
 create mode 100644 ascd/ascd/cdcontrol.c
 create mode 100644 ascd/ascd/config.h
 create mode 100755 ascd/ascd/configure
 create mode 100644 ascd/ascd/dirs.h
 create mode 100644 ascd/ascd/doc/CHANGES
 create mode 100644 ascd/ascd/doc/NEWS
 create mode 100644 ascd/ascd/ext.h
 create mode 100644 ascd/ascd/faktory.c
 create mode 100644 ascd/ascd/faktory_prot.h
 create mode 100644 ascd/ascd/handlers.c
 create mode 100644 ascd/ascd/misc.c
 create mode 100644 ascd/ascd/misc/config.h.default
 create mode 100755 ascd/ascd/misc/default
 create mode 100644 ascd/ascd/misc/dirs.h.default
 create mode 100644 ascd/ascd/mixer_ext.h
 create mode 100644 ascd/ascd/mixer_global.c
 create mode 100644 ascd/ascd/mixer_handlers.c
 create mode 100644 ascd/ascd/themes/default.tar
 create mode 100644 ascd/ascd/themes/themes-manual.ps.gz
 create mode 100644 ascd/ascd/themes/themes.tar
 create mode 100644 ascd/ascd/version.h
 create mode 100644 ascd/ascd/wings_ext.h
 create mode 100644 ascd/ascd/wings_global.c
 create mode 100644 ascd/ascd/wings_main.c
 create mode 120000 ascd/ascd/workman/include
 create mode 100644 ascd/ascd/workman/wm_cdda.h
 create mode 100644 ascd/ascd/workman/wm_cddb.h
 create mode 100644 ascd/ascd/workman/wm_cdinfo.h
 create mode 100644 ascd/ascd/workman/wm_cdrom.h
 create mode 100644 ascd/ascd/workman/wm_config.h
 create mode 100644 ascd/ascd/workman/wm_database.h
 create mode 100644 ascd/ascd/workman/wm_helpers.h
 create mode 100644 ascd/ascd/workman/wm_index.h
 create mode 100644 ascd/ascd/workman/wm_platform.h
 create mode 100644 ascd/ascd/workman/wm_scsi.h
 create mode 100644 ascd/ascd/workman/wm_struct.h
 create mode 100644 ascd/ascd/workman/wm_version.h
 create mode 100644 ascd/ascd/workman/workman.h
 create mode 100644 ascd/ascd/workman/workman_defs.h
 create mode 100755 ascd/configure
 create mode 100755 ascd/default
 create mode 100644 ascd/libworkman/COPYING.LIB
 create mode 100644 ascd/libworkman/Config
 create mode 100644 ascd/libworkman/Imakefile
 create mode 100644 ascd/libworkman/README
 create mode 100644 ascd/libworkman/buildindex.c
 create mode 100644 ascd/libworkman/cddaslave.c
 create mode 100644 ascd/libworkman/cddb.c
 create mode 100644 ascd/libworkman/cdinfo.c
 create mode 100644 ascd/libworkman/cdrom.c
 create mode 100644 ascd/libworkman/database.c
 create mode 100644 ascd/libworkman/drv_sony.c
 create mode 100644 ascd/libworkman/drv_toshiba.c
 create mode 100644 ascd/libworkman/include/wm_cdda.h
 create mode 100644 ascd/libworkman/include/wm_cddb.h
 create mode 100644 ascd/libworkman/include/wm_cdinfo.h
 create mode 100644 ascd/libworkman/include/wm_cdrom.h
 create mode 100644 ascd/libworkman/include/wm_config.h
 create mode 100644 ascd/libworkman/include/wm_database.h
 create mode 100644 ascd/libworkman/include/wm_helpers.h
 create mode 100644 ascd/libworkman/include/wm_index.h
 create mode 100644 ascd/libworkman/include/wm_platform.h
 create mode 100644 ascd/libworkman/include/wm_scsi.h
 create mode 100644 ascd/libworkman/include/wm_struct.h
 create mode 100644 ascd/libworkman/include/wm_version.h
 create mode 100644 ascd/libworkman/include/workman.h
 create mode 100644 ascd/libworkman/include/workman_defs.h
 create mode 100644 ascd/libworkman/index.c
 create mode 100644 ascd/libworkman/plat_aix.c
 create mode 100644 ascd/libworkman/plat_bsd386.c
 create mode 100644 ascd/libworkman/plat_freebsd.c
 create mode 100644 ascd/libworkman/plat_hpux.c
 create mode 100644 ascd/libworkman/plat_irix.c
 create mode 100644 ascd/libworkman/plat_linux.c
 create mode 100644 ascd/libworkman/plat_linux_audio.c
 create mode 100644 ascd/libworkman/plat_linux_cdda.c
 create mode 100644 ascd/libworkman/plat_news.c
 create mode 100644 ascd/libworkman/plat_openbsd.c
 create mode 100644 ascd/libworkman/plat_osf1.c
 create mode 100644 ascd/libworkman/plat_sun.c
 create mode 100644 ascd/libworkman/plat_sun_audio.c
 create mode 100644 ascd/libworkman/plat_sun_cdda.c
 create mode 100644 ascd/libworkman/plat_svr4.c
 create mode 100644 ascd/libworkman/plat_template.c
 create mode 100644 ascd/libworkman/plat_ultrix.c
 create mode 100644 ascd/libworkman/scsi.c
 create mode 100644 ascd/libworkman/wm_helpers.c
 create mode 100644 ascd/version.h

diff --git a/ascd/Imakefile b/ascd/Imakefile
new file mode 100644
index 0000000..f3ae0fb
--- /dev/null
+++ b/ascd/Imakefile
@@ -0,0 +1,7 @@
+#define IHaveSubdirs
+#define PassCDebugFlags
+
+SUBDIRS = libworkman ascd
+
+MakeSubdirs($(SUBDIRS))
+DependSubdirs($(SUBDIRS))
diff --git a/ascd/README b/ascd/README
new file mode 100644
index 0000000..f810fc8
--- /dev/null
+++ b/ascd/README
@@ -0,0 +1,50 @@
+This archive includes the original ascd 0.13.2 archive and the latest
+LibWorkMan snapshot (990620). To compile the software:
+
+  - run ./configure (*)
+  - make
+  - make install
+  - make install.man
+
+(*) configure will ask you a few question about ascd options. If you want
+to use the default settings, run ./default instead of ./configure.
+
+Important note:
+---------------
+
+I changed two things in libworkman:
+
+1) I added #under OSS_SUPPORT in plat_linux.c in order to definitly 
+prevent libworkman to use the mixer.
+
+2) I added wm_cddb.h in installed files as it was missing in the
+original tarball.
+
+Upgrading from ascd <= 0.12.X
+-----------------------------
+
+Ascd 0.13 uses the last libworkman snapshot and is *not* compatible
+with the previous ones. Do not forget to compile and install the
+snapsnot included in this archive before trying to compile ascd 0.13.
+
+More infos:
+-----------
+
+I strongly suggest to read the following texts:
+
+  - in libworkman/ dir, read the README file and edit Config file.
+  - in ascd/ dir, read the README file.
+
+AScd:
+-----
+
+  * maintained by Denis Bourez <[email protected]>
+  * homepage: http://worldserver.oleane.com/rsn/ascd-en.html
+
+LibWorkMan:
+-----------
+
+  * maintained by Dirk Försterling <[email protected]>
+  * homepage: http://www.DeathsDoor.com/milliByte/WorkMan/
+
+Denis Bourez, 99/9/5
diff --git a/ascd/ascd/Imakefile b/ascd/ascd/Imakefile
new file mode 100644
index 0000000..b032d30
--- /dev/null
+++ b/ascd/ascd/Imakefile
@@ -0,0 +1,63 @@
+#include "config.h"
+#include "dirs.h"
+
+NAME 		= ascd
+BINDIR		= CBINDIR
+MANDIR		= CMANDIR
+THEMESDIR	= CTHDIR
+MOREFLAGS	= -I/usr/local/include -I/usr/X11R6/include -I.
+CCOPTIONS	= $(MOREFLAGS)
+SYS_LIBRARIES	= $(XLIB) -lXpm
+
+LOCAL_LIBRARIES = -L../libworkman -lworkman -L/usr/local/lib
+
+SRCS_OPT1 =
+SRCS_OPT2 =
+SRCS_OPT3 =
+OBJS_OPT1 =
+OBJS_OPT2 =
+OBJS_OPT3 =
+
+#ifdef WMK
+SRCS_OPT1 = wings_main.c
+OBJS_OPT1 = wings_main.o
+#endif
+
+#ifdef MIXER
+SRCS_OPT2 = mixer_handlers.c
+OBJS_OPT2 = mixer_handlers.o
+#endif
+
+SRCS = 	cdcontrol.c misc.c faktory.c handlers.c \
+	$(SRCS_OPT1) \
+	$(SRCS_OPT2) \
+	$(SRCS_OPT3) \
+	ascd.c
+
+OBJS = 	cdcontrol.o misc.o faktory.o handlers.o \
+	$(OBJS_OPT1) \
+	$(OBJS_OPT2) \
+	$(OBJS_OPT3) \
+	ascd.o
+
+#ifdef WMK
+WM_LIB = -lm -lz -lpng -ljpeg -ltiff -lgif -lPropList -lwraster -lWINGs
+#else
+WM_LIB = 
+#endif
+
+AllTarget($(NAME))
+NormalProgramTarget($(NAME),$(OBJS),NullParameter,$(SYS_LIBRARIES) $(LOCAL_LIBRARIES) $(WM_LIB),NullParameter)
+InstallProgram($(NAME),$(BINDIR))
+InstallManPage($(NAME),$(MANDIR))
+InstallMultiple(themes/themes-manual.ps.gz,$(THEMESDIR))
+
+install::
+	@echo "Installing themes..."
+	@cp themes/themes.tar $(THEMESDIR)
+	@cp themes/default.tar $(THEMESDIR)
+	@cd $(THEMESDIR) ; tar xf themes.tar
+	@cd $(THEMESDIR) ; tar xf default.tar
+	@cd $(THEMESDIR) ; rm themes.tar
+	@cd $(THEMESDIR) ; rm default.tar
+	@echo "Done."
diff --git a/ascd/ascd/README b/ascd/ascd/README
new file mode 100644
index 0000000..609ab50
--- /dev/null
+++ b/ascd/ascd/README
@@ -0,0 +1,138 @@
+========================================================================
+                                 AScd 0.13
+                 CD player for Afterstep and Window Maker
+========================================================================
+
+MAINTAINER:
+
+                 Denis Bourez:
+               - email: [email protected]
+               - AScd home page: http://worldserver.oleane.com/rsn/
+
+AScd ORIGINAL AUTHOR:
+
+                 Rob Malda:
+               - email: [email protected]
+               - WWW: http://CmdrTaco.net/
+
+========================================================================
+
+DESCRIPTION:
+
+AScd is a CD player that can be "docked" on the Afterstep Wharf or the
+Window Maker dock. It uses libworkman to access CD drivers.
+
+========================================================================
+
+HISTORY:
+
+AScd 0.1 -> 0.6
+---------------
+
+The original code was taken by Rob Malda from asclock (Afterstep wharf
+module, by Beat Christen <[email protected]>) and from workbone (CD
+driver). 
+
+AScd 0.7 -> 0.9
+---------------
+
+I removed all the workbone code (Linux specific): ascd could then be compiled
+on several UN*X flavors. I took a big part or the WorkMan (see at the end of
+this text) src and merged it with the ascd 0.4 interface. The WorkMan code
+was compiled as a library.
+
+AScd >= 0.10
+------------
+
+WorkMan has splitted in 2 parts: the ui and the lib, named libworkman. So,
+AScd uses this brand new lib, instead of the one which was included in the
+AScd's archive. WorkMan's maintainer is Dirk Foersterling
+([email protected]).
+
+AScd >= 0.12
+------------
+
+AScd no longer includes the pixmaps in the source files. It is now possible 
+to change the pixmaps theme at run time. AScd now needs a directory where
+to store the pixmaps. The default is /usr/local/share/AScd.
+
+AScd >= 0.13
+------------
+
+The theme files are now fully customizable. All theme directories are
+installed by default in /usr/local/share/AScd/Themes.
+
+========================================================================
+                             HOW TO COMPILE????
+========================================================================
+
+You need to download, compile and install the WorkMan library. Please
+use the 99/05/05 snapshot wish can be found on the AScd's home page or
+on the primary ftp site for WorkMan: 
+
+ftp://ftp.midwinter.com/WorkMan/snapshots/
+
+========================================================================
+                              CONFIGURATION
+========================================================================
+
+Run ./configure
+
+This script will ask you a few questions about compilation-time
+options, and generate a "config.h" file matching your choices. The
+script will then run xmkmf to generate the Makefile.
+
+========================================================================
+                          COMPILATION/INSTALLATION
+========================================================================
+
+IMPORTANT NOTE: if you're upgrading from AScd 0.12, please remove
+everything in the AScd default directory (/usr/local/share/AScd) as the
+theme files are *not* compatible.
+
+    make
+  
+  - If you want to try the program, just type
+    
+    ./ascd
+    
+    (try './ascd -help' to see the commandline options)
+
+    If everything is ok, you can install the program and the
+    man page:
+    
+    make install
+    make install.man
+
+
+* installation for AfterStep
+  --------------------------
+
+  Add a line in your ~/.steprc file:
+
+  *Wharf - - Swallow "ascd" /usr/local/bin/ascd -device /dev/wcd0a &
+
+  (customized to your system settings of course!)
+  
+* installation for Window Maker
+  -----------------------------
+
+  Launch ascd with the "+w" option.
+  Dock ascd by dragging it to the dock.
+
+========================================================================
+                                CREDITS
+========================================================================
+
+  Denis Bourez ([email protected]) http://worldserver.oleane.com/rsn/
+  Rob Malda ([email protected])  http://CmdrTaco.net/
+
+  The 0.12 *digits.xpm were taken and modified from wmitime, an excellent
+  clock applet for Window Maker. Wmitime was written by Dave Clark
+  ([email protected]) More info at: http://www.neotokyo.org. I added the
+  lower case letters.
+  
+  The new 0.13 default font is fully 8bit compatible! It has been drawn
+  by Stefan Zeiger ([email protected]).
+========================================================================
+                                                Denis Bourez, 05/17/1999
diff --git a/ascd/ascd/ascd.c b/ascd/ascd/ascd.c
new file mode 100644
index 0000000..394cf95
--- /dev/null
+++ b/ascd/ascd/ascd.c
@@ -0,0 +1,764 @@
+/* ===========================================================================
+ * AScd: the AfterStep and WindowMaker CD player
+ * ascd.c: main source
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <X11/Xlib.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
+
+#include "config.h"
+#include "ascd.h"
+#include "faktory_prot.h"
+#include <workman/workman.h>
+
+XpmIcon alphaXPM;
+XpmIcon ralphaXPM;
+XpmIcon backXPM;
+XpmIcon iconXPM;
+
+/* External functions */
+
+extern time();
+extern open();
+extern cd_control(int);
+extern cd_control_version();
+
+/* the various switches: */
+
+int debug = FALSE;                     /* verbose mode */
+unsigned int autoplay = 0;         /* if set, play a disk when it is inserted -> see -a c.l. option  */
+unsigned int autoprobe = TRUE;     /* probe the drive, but do not play (autoplay) */
+unsigned int intro_mode = 0;       /* play only the beginning of the tracks */
+unsigned int autorepeat = FALSE;   /* zzzzzzzzzzzzz */
+unsigned int blind_mode = FALSE;   /* no counter updates = no drive LED flashes */
+unsigned int loop_mode = 0;        /* loop from loop_1 to loop_2 */
+unsigned int loop_start_track = 0; /* track of the beggining of the loop */
+unsigned int loop_end_track =0;    /* track of the end of the loop */
+unsigned int loop_1 = 0;           /* pos. 1 */
+unsigned int loop_2 = 0;           /* pos. 2 */
+unsigned int show_db = FALSE;      /* do we have to scroll song names? */
+unsigned int show_artist = FALSE;  /* if show_db, do you append artist name? */
+unsigned int show_db_pos = 0;      /* internal, used for the song name scrolling */
+unsigned int show_icon_db_pos = 0; /* internal, used for the song name scrolling */
+unsigned int force_upper = FALSE;  /* all messages must be in uppercase */
+int ignore_avoid = FALSE;          /* if set, we play *all* tracks, ignoring 'avoid' tags in database */
+unsigned int fast_track = 0;       /* the fast track select method */
+unsigned int xflaunch = FALSE;     /* do we launch xfascd when right click on the display? */
+unsigned int time_mode = 0;        /* display mode for the counter. see function RedrawWindow() */
+unsigned int cue_time = 10;        /* nbr of seconds for cue -> see -c command line option */
+
+/* internals */
+
+int lasttime = -1;
+extern char *cd_device;            /* the hardware device pointing to the CD ROM player */
+unsigned int datatrack = 0;        /* is the current track a data track ? */
+unsigned int direct_access = 0;    /* pos. to reach with the ruler */
+unsigned int direct_track = 0;     /* if we want to go directly to another track */
+unsigned int wanna_play = FALSE;
+unsigned int do_autorepeat = FALSE;
+int wanted_track = 0;
+unsigned int old_track = 0;
+unsigned int anomalie = 0;         /* cd_control return value */
+
+/* let's talk about X... */
+
+Display *Disp;
+Window Root;
+Window Iconwin;
+Window Win;
+char *Geometry = 0;
+char device[128]=DEFAULTDEVICE;
+char xv[128];
+int withdrawn=FALSE;
+GC WinGC;
+int CarrierOn = FALSE;
+int screen;
+
+/* everything dealing with the hardware volume: */
+
+int volume = MAX_VOL ;             /* CD volume */
+int muted_volume = 0;              /* CD volume in muted mode */
+int unmuted_volume = MAX_VOL;      /* CD volume to restore when leaving muted mode */
+unsigned int muted = FALSE;        /* is the CD muted? */
+unsigned int fade_out = FALSE;     /* do you have to start a fade in/out? */
+unsigned int fade_step = 5;        /* the fading speed */
+unsigned int fade_ok = 0;          /* can't remember. sorry!!!! */
+extern int min_volume,max_volume;  /* from LibWorkMan */
+int cur_balance = 10;              /* ? */
+
+char led_text[9];                  /* the 'help' messages */
+unsigned int text_timeout = 1;     /* messages timemout in seconds */
+long text_start = 0;               /* timeout */
+
+/* misc, have to add explanations here...! */
+
+int selectors_timeout = 0;
+int redraw = TRUE;
+int has_clicked = FALSE;
+int info_modified = 0;
+int big_spaces = 0;
+int slow_down = 0;
+
+/* ====================================================================
+   Faktory: theme subsystem
+   ==================================================================== */
+
+char theme[FAK_CMAX];
+char selected_theme[FAK_CMAX];
+unsigned int theme_select = 0;
+unsigned int theme_select_nbr = 0;
+
+unsigned int panel = 1;           /* current panel */
+unsigned int panels = 0;          /* how many panels? */
+unsigned int but_max = 0;         /* how many buttons? */
+unsigned int but_msg = 0;         /* which one is the message zone? */
+unsigned int but_counter = 0;     /* which one is the counter? */
+unsigned int but_tracknbr = 0;    /* which one is the track number? */
+unsigned int but_db = 0;          /* which one is the database zone? */
+unsigned int icon_msg = 0;
+unsigned int icon_counter = 0;
+unsigned int icon_tracknbr = 0;
+unsigned int but_current = 0;     /* last selected button */
+
+unsigned int panel_stop = 0;      /* autoswitch to this panel on stop/eject */
+unsigned int panel_play = 0;      /* autoswitch to this panel on play */
+
+char th_name[FAK_CMAX];
+char th_author[FAK_CMAX];
+char th_release[FAK_CMAX];
+char th_email[FAK_CMAX];
+char th_url[FAK_CMAX];
+char th_comment[FAK_CMAX];
+char th_alpha1[FAK_CMAX];
+char th_alpha2[FAK_CMAX];
+char th_background[FAK_CMAX];
+char th_icon_window[FAK_CMAX];
+
+int th_no_minus = FALSE; 
+int th_no_icon_window = FALSE;
+
+struct fak_button thdata[FAK_BMAX];
+
+/*****************************************************************************/
+
+/* ====================================================================
+   The optional modules globals:
+   ==================================================================== */
+
+#ifdef WMK
+#include "wings_global.c"
+#endif
+
+#ifdef MIXER
+#include "mixer_global.c"
+#endif
+
+/*****************************************************************************/
+
+void mouse_events(XEvent Event) 
+{
+    int i;
+    int j;
+    int do_it = TRUE;
+
+    for (i=1 ; i <= but_max ; i++) {
+	if ((thdata[i].panel == panel) || (thdata[i].panel == 0)) {
+	    if (strlen(thdata[i].xpm_file) > 0) {
+		if ((Event.xbutton.y >= thdata[i].y) && (Event.xbutton.y <= thdata[i].y + thdata[i].xpm.attributes.height)) {
+		    if ((Event.xbutton.x >= thdata[i].x) && (Event.xbutton.x <= thdata[i].x + thdata[i].xpm.attributes.width)) {
+			if (th_no_icon_window) {
+			    /* Special Icon Mode: we must check in which window the user clicked */
+			    do_it = TRUE;
+			    if (Event.xbutton.window == Iconwin) {
+				if (!thdata[i].icon) do_it = FALSE;
+			    } else {
+				if (thdata[i].icon) do_it = FALSE;
+			    }
+			}
+			/* no commands defined? We skip this one! */
+			if (((thdata[i].left != 0) || (thdata[i].mid != 0) || (thdata[i].right != 0)) && (do_it)) {
+			    if (debug) fprintf(stderr, "** User selected button %d with mouse button %d\n", i, Event.xbutton.button);
+			    j = 0;
+			    switch(Event.xbutton.button) {
+			    case 1: j = thdata[i].left; break;
+			    case 2: j = thdata[i].mid; break;
+			    default: j = thdata[i].right; break;
+			    }
+			    but_current = i;
+			    if (debug) fprintf(stderr, "-> command = %d\n", j);
+			    if ((j >= 1) && (j <= 49)) fak_event_handle(j, Event);
+			    else if ((j >= 50) && (j <= 99)) cd_event_handle(j, Event);
+#ifdef MIXER
+			    else if ((j >= 100) && (j <= 199)) mixer_event_handle(j, Event);
+#endif
+			    break;
+			}
+		    }
+		}
+	    }
+	}
+    }
+
+} 
+
+/* ------------------------------------------------------------------------
+   GUI control
+   ------------------------------------------------------------------------ */
+
+Pixel get_color(char *ColorName) 
+{
+    XColor Color;
+    XWindowAttributes Attributes;
+
+    XGetWindowAttributes(Disp,Root,&Attributes);
+    Color.pixel = 0;
+
+    if (!XParseColor (Disp, Attributes.colormap, ColorName, &Color))
+      fprintf(stderr,"ascd: can't parse %s\n", ColorName);
+    else if(!XAllocColor (Disp, Attributes.colormap, &Color))
+      fprintf(stderr,"ascd: can't allocate %s\n", ColorName);
+
+    return Color.pixel;
+} 
+
+void create_window(int argc, char **argv) 
+{
+    int i;
+    unsigned int borderwidth ;
+    char *display_name = NULL;
+    char *wname = "AScd";
+    XGCValues gcv;
+    unsigned long gcm;
+    XTextProperty name;
+    Pixel back_pix, fore_pix;
+    int x_fd;
+    int d_depth;
+    int ScreenWidth, ScreenHeight;
+    XSizeHints SizeHints;
+    XWMHints WmHints;
+    XClassHint classHint;
+
+    /* Open display */
+    if (!(Disp = XOpenDisplay(display_name))) {
+	fprintf(stderr,"ascd: can't open display %s\n", XDisplayName(display_name));
+	exit (1);
+    }
+
+    screen = DefaultScreen(Disp);
+
+#ifdef WMK
+    scr = WMCreateScreen(Disp, DefaultScreen(Disp));
+    create_big_window(scr);
+    create_db_window(scr);
+    create_about_window(scr);
+#endif
+
+    Root = RootWindow(Disp, screen);
+    d_depth = DefaultDepth(Disp, screen);
+    x_fd = XConnectionNumber(Disp);
+    ScreenHeight = DisplayHeight(Disp,screen);
+    ScreenWidth = DisplayWidth(Disp,screen);
+
+    /* it's time to load the visual theme! */
+
+    if (fak_load_theme(theme, FALSE)) {
+       if (debug) fprintf(stderr, "-> back from fak_load_theme!\n");
+    } else {
+	fprintf(stderr, "ascd: fatal error\n\n");
+	fprintf(stderr, "The '%s' theme definition file can't be read.\n\n", theme);
+	exit(1);
+    }
+
+    SizeHints.flags= USSize|USPosition;
+    SizeHints.x = 0;
+    SizeHints.y = 0;
+    back_pix = get_color("black");
+    fore_pix = get_color("white");
+
+    XWMGeometry(Disp, screen, Geometry, NULL, (borderwidth = 1), &SizeHints,
+		&SizeHints.x,&SizeHints.y,&SizeHints.width,
+		&SizeHints.height, &i);
+
+    SizeHints.width = backXPM.attributes.width;
+    SizeHints.height= backXPM.attributes.height;
+
+    Win = XCreateSimpleWindow(Disp, Root,
+			      SizeHints.x,
+			      SizeHints.y,
+			      SizeHints.width,
+			      SizeHints.height,
+			      borderwidth,
+			      fore_pix,
+			      back_pix);
+
+    if (!th_no_icon_window) {
+	Iconwin = XCreateSimpleWindow(Disp,Win,
+				      SizeHints.x,SizeHints.y,
+				      SizeHints.width,
+				      SizeHints.height,
+				      borderwidth,
+				      fore_pix,
+				      back_pix);
+    } else {
+	Iconwin = XCreateSimpleWindow(Disp, Win, 
+				      SizeHints.x,
+				      SizeHints.y,
+				      iconXPM.attributes.width,
+				      iconXPM.attributes.height,
+				      borderwidth,
+				      fore_pix,
+				      back_pix);
+    }
+
+    XSetWMNormalHints(Disp, Win, &SizeHints);
+
+    classHint.res_name = "ascd" ;
+    classHint.res_class = "AScd";
+
+    XSetClassHint (Disp, Win, &classHint);
+
+    XSelectInput(Disp, Win, (ExposureMask | ButtonPressMask | StructureNotifyMask));
+
+    XSelectInput(Disp, Iconwin, (ExposureMask | ButtonPressMask |
+				 StructureNotifyMask));
+    
+    if (XStringListToTextProperty(&wname, 1, &name) ==0) {
+	fprintf(stderr, "ascd: can't allocate window name\n");
+	exit(-1);
+    }
+
+    XSetWMName(Disp, Win, &name);
+
+    /* Create WinGC */
+    gcm = GCForeground|GCBackground|GCGraphicsExposures;
+    gcv.foreground = fore_pix;
+    gcv.background = back_pix;
+    gcv.graphics_exposures = False;
+    WinGC = XCreateGC(Disp, Root, gcm, &gcv);
+
+    WmHints.initial_state = withdrawn ? WithdrawnState : NormalState ;
+    WmHints.icon_window = Iconwin;
+    WmHints.window_group = Win;
+    WmHints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
+    WmHints.icon_x = SizeHints.x;
+    WmHints.icon_y = SizeHints.y;
+    XSetWMHints(Disp, Win, &WmHints);
+    XSetCommand(Disp, Win, argv, argc);
+    
+    XMapWindow(Disp, Win);
+
+    
+    if (debug) fprintf(stderr, "-> calling fak_maskset() from create_window()...\n");
+    fak_maskset();
+    if (debug) fprintf(stderr, "-> calling fak_redraw() from create_window()...\n");
+    fak_redraw();
+    if (debug) fprintf(stderr, "-> Leaving create_window()\n");
+} 
+
+void newtext(char *txt) { 
+    strcpy(led_text, txt);
+    text_start = 0;
+} 
+
+void show_icon_db_f() {
+    /* scroll the song title */
+    char txt[256];
+    char txt2[256];
+    char dsp[9];
+    int track;
+    int red;
+
+    if (!th_no_icon_window) return;
+    if (icon_msg == 0) return;
+
+    if (theme_select == 0) {
+
+	/* there is a message to display, we'll wait for the message to
+	   time-out before handling the track title... */
+
+	if (strlen(led_text) > 0) return;
+
+	/* if in fast track selector, we show the title of the track to reach */
+
+	if (fast_track > 0) {
+	    track = fast_track - 1;
+	    red = FALSE;
+	} else {
+	    track = cur_track - 1;
+	    red = TRUE;
+	}
+
+        if (track > cur_ntracks - 1) return;
+       
+	if (cd->trk[track].songname != NULL) {
+	    if (show_artist) {
+		if (cd->artist != NULL) {
+		    sprintf(txt2, "%s: %s", cd->artist, cd->trk[track].songname);
+		} else {
+		    strcpy(txt2, cd->trk[track].songname);
+		}
+	    } else {
+		strcpy(txt2, cd->trk[track].songname);
+	    }
+	    fak_icon_text("", MSG_PANEL, 0, red);
+	    if (strlen(txt2) > thdata[icon_msg].w) {
+		strcpy(txt, "       ");
+		strcat(txt, txt2);
+		if (show_icon_db_pos > strlen(txt)) show_icon_db_pos = 0;
+		tes_sncpy(dsp, txt + show_icon_db_pos, thdata[icon_msg].w);
+		fak_icon_text(dsp, MSG_PANEL, 0, red);
+		show_icon_db_pos ++;
+	    } else {
+		fak_icon_text(txt2, MSG_PANEL, 0, red);
+	    }
+	} else {
+	    strcpy(txt, "");
+	}
+    }
+}
+
+void show_db_f() {
+    /* scroll the song title */
+    char txt[256];
+    char txt2[256];
+    char dsp[9];
+    int track;
+    int red;
+    int where;
+    int longueur;
+    int i;
+
+    if (but_db > 0) {
+	where = DB_PANEL;
+	longueur = thdata[but_db].w;
+    } else {
+	where = MSG_PANEL;
+	longueur = thdata[but_msg].w;
+    }
+
+    if ((theme_select == 0) || (where == DB_PANEL)) {
+
+	/* 
+	   there is a message to display, we'll wait for the message
+	   to time-out before handling the track title... (but only if
+	   there is no separate areas) 
+	*/
+
+	if ((strlen(led_text) > 0) && (where == MSG_PANEL)) return;
+
+	/* if in fast track selector, we show the title of the track to reach */
+
+	if (fast_track > 0) {
+	    track = fast_track - 1;
+	    red = FALSE;
+	} else {
+	    track = cur_track - 1;
+	    red = TRUE;
+	}
+
+	/* warning: avoid weird datas: */
+
+        if (track > cur_ntracks - 1) return;
+	if (track < 0) return;
+       
+       	if (debug > 1) fprintf(stderr, "** Show DB TRACK = %d\n", track);
+
+	if (cd->trk[track].songname != NULL) {
+	    if (show_artist) {
+		if (cd->artist != NULL) {
+		    sprintf(txt2, "%s: %s", cd->artist, cd->trk[track].songname);
+		} else {
+		    strcpy(txt2, cd->trk[track].songname);
+		}
+	    } else {
+		strcpy(txt2, cd->trk[track].songname);
+	    }
+	    fak_text("", where, 0, red);
+	    if (strlen(txt2) > longueur) {
+		strcpy(txt, "");
+		for (i = 0 ;  i < longueur ; i++) strcat(txt, " ");
+		strcat(txt, txt2);
+		if (show_db_pos > strlen(txt)) show_db_pos = 0;
+		tes_sncpy(dsp, txt + show_db_pos, longueur);
+		fak_text(dsp, DB_PANEL, 0, red);
+		show_db_pos ++;
+	    } else {
+		fak_text(txt2, DB_PANEL, 0, red);
+	    }
+	} else {
+	    strcpy(txt, "");
+	}
+    }
+
+    show_icon_db_f();
+}
+
+/* ------------------------------------------------------------------------
+   Loooooooooooooooooping.........
+   ------------------------------------------------------------------------ */
+
+void main_loop() 
+{
+    unsigned int no_disk = 0;
+    long int dodo = RDTIME;
+    XEvent Event;
+
+    while(1) {
+       
+        if (debug > 1) fprintf(stderr, "** [Main Loop] mode = %02d track = %02d \n", cur_cdmode, cur_track);
+       
+	if (cur_cdmode == WM_CDM_EJECTED) no_disk = 1;
+
+	slow_down++;
+	
+	if (slow_down > 10) {
+
+#ifdef WMK
+	    /* in 0.13, we no longer close WINGs windows if ejected
+	    if ((cur_cdmode == WM_CDM_EJECTED) && (en_vue)) {
+		WMCloseWindow(win);
+		en_vue = FALSE;
+	    }
+	    */
+#endif
+
+	    if (no_disk == 1) {
+		cur_ntracks = 0; /* 0.13pr6: what a hack!!!! */
+
+		if (autoplay || autoprobe) {
+		    dodo = RDTIME2;
+		    wm_cd_status();
+		}
+
+		if (cur_cdmode != WM_CDM_EJECTED) no_disk = 0;
+		if ( (cur_cdmode == WM_CDM_STOPPED) && (autoplay) )  {
+		    newtext("Autoplay");
+		    if (cd->volume > 0) {
+			volume=cd->volume;
+			cd_volume(volume, 10, max_volume);
+		    }
+		    cd_control(PLAY);
+		    wm_cd_status();
+		    dodo = RDTIME;
+		    fak_maskset();
+		    fak_redraw();
+		}
+	    }
+	    
+	    /* The Loop Mode : */
+	    if ( (cur_track == loop_end_track ) && (cur_pos_rel >= loop_2) && (loop_mode) ) {
+		cd_control(LOOP);
+		fak_redraw();
+	    }
+	    
+	    /* The Intro Scan Mode : */
+	    if ( (cur_pos_rel > cue_time) && (intro_mode) ) {
+		cd_control(INTRONEXT);
+		fak_redraw();
+	    }
+	}
+
+	if ((slow_down == 1) || (slow_down == 6)) {
+	    if ((show_db) && (cur_cdmode == WM_CDM_PLAYING)) show_db_f();
+	}
+
+	/* Check events */
+	
+	while (XPending(Disp))
+	    {
+		
+		XNextEvent(Disp, &Event);
+		
+#ifdef WMK		
+		if (!WMHandleEvent(&Event)) {
+#endif
+		switch(Event.type) {
+		    
+		    /* ---------------------- Redraw Window --------------------- */
+		    
+		case Expose:
+		    if(Event.xexpose.count == 0) {
+			lasttime=01;
+			redraw = TRUE;
+			fak_redraw();
+		    } else {
+			if (debug > 1) fprintf(stderr, "** XEVent - expose, not handled, count = %d\n", Event.xexpose.count);
+		    }
+		    break;
+		    
+		    /* ----------------------- Mouse Click ---------------------- */
+		    
+		case ButtonPress:
+		    
+		    wm_cd_status();
+		    mouse_events(Event);
+		    break;
+		    
+		    /* ------------------------ Destroy Window ------------------- */
+		    
+		case DestroyNotify:
+		    XFreeGC(Disp, WinGC);
+		    XDestroyWindow(Disp, Win);
+		    XDestroyWindow(Disp, Iconwin);
+		    XCloseDisplay(Disp);
+		    exit(0);
+		    break;
+		default:
+		    if (debug > 1) fprintf(stderr, "** XEvent - unknown event type = %d\n", Event.type);
+		    break;
+		}
+
+#ifdef WMK		
+		}
+#endif
+		XFlush(Disp);
+	    } /* check event */
+	
+	usleep(dodo);
+	
+	/* ----------------- now we have to redraw the screen: ---------------- */
+	
+	if ((slow_down > 10) || (has_clicked)) {
+	    fak_redraw();
+	    slow_down = 0;
+	    has_clicked = FALSE;
+	}
+    }
+} 
+
+/* ------------------------------------------------------------------------
+   So... let's go!
+   ------------------------------------------------------------------------ */
+
+int main(int argc, char *argv[]) 
+{
+    extern char *rcfile, *dbfiles;  
+
+    /*printf("AScd %s\n", VERSION);*/
+    
+    /* CD device: */
+
+#ifndef NO_D_DEVICE
+    cd_device = malloc(strlen(DEFAULTDEVICE) + 1);
+    strcpy(cd_device, DEFAULTDEVICE);
+#endif
+
+    strcpy(led_text, "");
+    strcpy(theme, "default");
+    strcpy(xv, "xv");
+    
+    /* the WorkMan database. It's still not used in ascd, but we need this
+       to start the WorkMan code
+    */
+
+    rcfile = getenv("WORKMANRC");
+    if (rcfile) rcfile = (char *)wm_strdup(rcfile);
+    dbfiles = getenv("WORKMANDB");
+    if (dbfiles) dbfiles = (char *)wm_strdup(dbfiles);
+    split_workmandb();
+
+    load_rc_file(FALSE);
+    command_line_parse(argc, argv);
+
+#ifdef WMK
+   if (debug) fprintf(stderr, "** [WINGs] init app...\n");
+    WMInitializeApplication("AScd", &argc, argv);
+#endif
+
+#ifdef MIXER
+    mixer_ok = 1;
+   if (debug) fprintf(stderr, "** [mixer] checking mixer device...\n");
+    if (! check_mixer()) {
+        fprintf(stderr, "ascd: can't initialize mixer device. Mixing support disabled.\n");
+	mixer_ok = 0;
+    }
+#endif
+
+   if (debug) fprintf(stderr, "** creating main window...\n");
+   create_window(argc, argv);
+   if (debug) fprintf(stderr, "** checking CD status...\n");
+   wm_cd_status();
+    
+    if (cur_cdmode != WM_CDM_EJECTED) {
+       if (debug) fprintf(stderr, "** reading CD initial volume...\n");
+	volume = wm_cd_read_initial_volume(max_volume);
+    } else {
+	if (debug) fprintf(stderr, "** CD is ejected: volume ignored.\n");
+    }
+
+    if (debug) fprintf(stderr, "** checking autoplay...\n");
+    if ((autoplay) && (cur_cdmode == WM_CDM_STOPPED)) {
+	if (cur_track < 1) {
+	    cur_track = 1;
+	    wm_cd_status();
+	}
+       if (debug) fprintf(stderr, "-> autoplay: play command\n");
+	cd_control(PLAY);
+       if (debug) fprintf(stderr, "-> autoplay: checking status\n");
+	wm_cd_status();
+	if (debug) fprintf(stderr, "-> autoplay: redrawing\n");
+	fak_redraw();
+       if (debug) fprintf(stderr, "-> autoplay: done\n");
+    } else {
+       if (debug) fprintf(stderr, "-> no autoplay\n");
+    }
+
+   if (debug) fprintf(stderr, "** checking current CD volume in database\n");
+   
+    if (((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) && (cd->volume > 0)) {
+	volume=cd->volume;
+	cd_volume(volume, 10, max_volume);
+    }
+   
+    fak_maskset();
+
+    if (debug) fprintf(stderr, "** Init passed. Entering main loop.\n");
+    redraw = TRUE;
+    main_loop();
+    return 0;
+}
diff --git a/ascd/ascd/ascd.h b/ascd/ascd/ascd.h
new file mode 100644
index 0000000..27d5920
--- /dev/null
+++ b/ascd/ascd/ascd.h
@@ -0,0 +1,186 @@
+#ifndef FALSE
+# define FALSE 0
+#endif
+#ifndef TRUE
+# define TRUE 1
+#endif
+
+/* These #defines control the main loop timeouts */
+#define RDTIME 50000L
+#define RDTIME2 75000L
+
+#define MAX_VOL 255
+
+
+/*#define ACCTABLE "éÉèÈêÊàÀâÂùÙûÛîÎôÔçÇ**"*/
+#define ACCTABLE "éeèeêeàaâaùuûuîiôoçc**"
+#define UPACCTABLE "éEèEêEàAâAùUûUîIôOçC**"
+
+
+#define COUNTER_PANEL 0
+#define MSG_PANEL 1
+#define TRACK_PANEL 2
+#define DB_PANEL 3
+
+/* ---------------------- CD control ---------------------- */
+
+#define PLAY 0
+#define PAUSE 1
+#define STOP 2
+#define UPTRACK 3
+#define DNTRACK 4
+#define CUE 5
+#define REV 6
+#define FIRST 7
+#define LAST 8
+#define LOOP 9
+#define DIRECTACCESS 10
+#define INTROSCAN 11
+#define INTRONEXT 12
+#define LOCACCESS 13
+#define DIRECTTRACK 14
+#define GLOBALACCESS 15
+
+/* CLOSETRAY added 990417 */
+#define CLOSETRAY 16
+
+/* new modes added in GMan experimentation. They're
+   also used in AScd >= 0.11 */
+#define STOPONLY 20
+#define EJECT 21
+
+/* ------------------ FAKTORY defines: ------------------- */
+
+#define FAK_BMAX 200             /* max buttons */
+#define FAK_CMAX 80              /* max lenght of infos strings */
+
+/* screen elements types: */
+
+#define FAK_PIXMAP           1
+#define FAK_COUNTER          2
+#define FAK_TRACKNBR         3
+#define FAK_MSG              4
+#define FAK_DB               5
+#define FAK_CD_BAR          10
+#define FAK_VCD_BAR         11
+#define FAK_ICD_BAR         12
+#define FAK_VOL_BAR         13
+#define FAK_VVOL_BAR        14
+#define FAK_IVOL_BAR        15
+#define FAK_MIXER_BAR       16
+#define FAK_VMIXER_BAR      17
+#define FAK_IMIXER_BAR      18
+
+/* 0132 new pixmap sliders: */
+
+#define FAK_CD_PIX          19
+#define FAK_VOL_PIX         20
+
+#define FAK_VVOL_PIX        21
+#define FAK_VCD_PIX         22
+
+/* general commands: the reserved range is 0 to 49 */
+
+#define FAK_PANEL_SWITCH     1
+#define FAK_QUIT             2
+#define FAK_PANEL1           3
+#define FAK_PANEL2           4
+#define FAK_PANEL3           5
+#define FAK_PANEL4           6
+#define FAK_PANEL5           7
+#define FAK_WINGS            8
+#define FAK_COUNTER_MODE     9
+#define FAK_TSELECT         10
+#define FAK_TNEXT           11
+#define FAK_TPREVIOUS       12
+#define FAK_FTSELECT        13
+#define FAK_FTNEXT          14
+#define FAK_FTPREVIOUS      15
+#define FAK_SAVE            16
+#define FAK_LOAD            17
+#define FAK_QREF            20
+
+/* general modes toggles: (not yet supported!!!) */
+
+#define FAK_TOG_AUTOPLAY    30
+#define FAK_TOG_AUTOREPEAT  31
+#define FAK_TOG_SHOWDB      32
+#define FAK_TOG_SHOWARTIST  33
+#define FAK_TOG_UPPER       34
+#define FAK_TOG_ISKIPS      35
+
+/* CD player commands: the reserved range is 50 to 99 */
+
+#define FAK_CD_PLAY         50
+#define FAK_CD_PAUSE        51
+#define FAK_CD_STOP         52
+#define FAK_CD_EJECT        53
+#define FAK_CD_STOPEJECT    54
+#define FAK_CD_EJECTQUIT    55
+
+#define FAK_CD_REW          60
+#define FAK_CD_FIRST        61
+#define FAK_CD_PREVIOUS     62
+#define FAK_CD_FWD          65
+#define FAK_CD_LAST         66
+#define FAK_CD_NEXT         67
+#define FAK_CD_DIRECT       68
+
+#define FAK_CD_LSTART       70
+#define FAK_CD_LEND         71
+#define FAK_CD_LOOP         72
+#define FAK_CD_GOLSTART     73
+#define FAK_CD_GOLEND       74
+#define FAK_CD_LTRACK       75
+#define FAK_CD_LTOTRACK     76
+#define FAK_CD_LFROMTRACK   77
+#define FAK_CD_LCLEAR       78
+
+#define FAK_CD_INTRO        80
+#define FAK_CD_FADE         81
+
+/* these two ones are not yet supported: */
+#define FAK_CD_RANDOM       82
+#define FAK_CD_RMODE        83
+
+#define FAK_CD_MUTE         90
+#define FAK_CD_VOLUME       91
+
+/* Mixer commands: the reserved range is 100 to 199 */
+
+#define FAK_MIXER_SET       100
+#define FAK_MIXER_50        101
+#define FAK_MIXER_75        102
+#define FAK_MIXER_100       103
+#define FAK_MIXER_0         104
+#define FAK_MIXER_LOAD      110
+#define FAK_MIXER_SAVE      111
+
+/* -------------------------------------------------------------------- */
+
+typedef struct _XpmIcon {
+    Pixmap pixmap;
+    Pixmap mask;
+    XpmAttributes attributes;
+} XpmIcon;
+
+struct fak_button
+{
+    unsigned int type;
+    unsigned int panel;
+    char xpm_file[FAK_CMAX];
+    char altxpm_file[FAK_CMAX];
+    XpmIcon xpm;
+    XpmIcon altxpm;
+    unsigned int left;
+    unsigned int right;
+    unsigned int mid;
+    unsigned int x;
+    unsigned int y;
+    unsigned int w;
+    unsigned int h;
+    unsigned int arg;
+    int icon;
+    unsigned int ox;
+    unsigned int oy;
+};
diff --git a/ascd/ascd/ascd.man b/ascd/ascd/ascd.man
new file mode 100644
index 0000000..3aa208f
--- /dev/null
+++ b/ascd/ascd/ascd.man
@@ -0,0 +1,267 @@
+.TH ascd 1 "17 may 1999" ascd
+.UC
+
+.SH NAME
+
+\fBascd\fP \- An AfterStep and WindowMaker CD player
+
+.SH SYNOPSIS
+
+.B ascd
+[
+.BI \-help
+]
+.br
+.B ascd
+[
+.BI \-version
+]
+.br
+.B ascd
+[
+.BI \-themes
+]
+.br
+.br
+.B ascd
+[
+.BI \-geometry " geometry"
+]
+[
+.BI \+/-withdrawn
+]
+[
+.BI \-device " device"
+]
+[
+.BI \-mixer " device"
+]
+[
+.BI \+/-a
+]
+[
+.BI \+/-p
+]
+[
+.BI \+/-t
+]
+[
+.BI \+/-B
+]
+[
+.BI \-cue " time"
+]
+[
+.BI \-ti " time"
+]
+[
+.BI \-volume " volume"
+]
+[
+.BI \-mvolume " volume"
+]
+[
+.BI \-maxvolume " volume"
+]
+[
+.BI \-minvolume " volume"
+]
+[
+.BI \-fadestep " value"
+]
+[
+.BI \-theme " theme directory"
+]
+[
+.BI \+/-ia
+]
+[
+.BI \+/-up
+]
+[
+.BI \-showtitle
+]
+[
+.BI \-showartist
+]
+[
+.BI \-debug
+]
+[
+.BI \-save
+]
+
+.SH DESCRIPTION
+
+\fBascd\fP is CD player written to play audio CD's integrated in
+afterstep's Wharf module or WindowMaker's dock. 
+\fBascd\fP handles mounted volumes, and opens and closes the tray.
+
+.SH CONFIGURATION OPTIONS
+
+.B -help, -h
+     print a list of commandline options and exit.
+
+.B -version
+
+     print the version number and exit.
+
+
+.B -themes
+
+     print the list of installed themes and exit.
+
+.B -geometry <geom>
+     specifies the geometry of \fBascd\fP. The Geometry
+     works like the standard X-Window geometry option.
+
+.B +/-withdrawn, +/-w
+    Turn on and off withdrawn mode. Use this option if
+    you use ascd with WindowMaker.
+
+.B -device <device>
+     specifies the CDROM device which should be used by 
+     ascd. If "-DNO_DEFAULT_DEVICE" was set at
+     compilation time this option will have no effect.
+
+.B -mixer <device>
+     specifies the mixer device. Works only if mixer
+     support was enabled at compilation.
+
+.B -cue <seconds>
+     specifies the time -in seconds- for \fBintro scan\fP
+     and \fBcue\fP modes.
+
+.B -ti <seconds>
+     specifies the timeout for the help texts. The
+     default is 1 second.
+
+.B +/-autoplay, +/-a
+     Turn on or off \fBautoplay mode\fP. When this is
+     turned on, ascd always wait for a CD, and play it 
+     automatically when inserted.
+
+.B +/-autoprobe, +/-p
+     Turn on or off \fBautoprobe mode\fP. This mode
+     checks for inserted CD. Default is on.
+
+.B +/-autorepeat, +/-t
+     autorepeat mode: when ascd reaches the end of the
+     last track, the CD is not stopped and first track
+     is played.
+
+.B +/-B
+     This option is no longer supported.
+
+.B -maxvolume <value>
+     Set the CD driver max volume.
+
+.B -minvolume <value>
+     Set the CD driver min volume.
+
+.B -volume <value>
+     Set the CD driver volume, between 0 and maxvolume.
+     Note that the -volume option in independant from 
+     the mixer. It controls the drive, not the sound
+     card.
+
+.B -mvolume <value>
+     Set the CD driver volume for the mute mode.
+
+.B -fadestep <value>
+     Set the fade in/out speed. Default is 5. Greater
+     values accelerate the fade.
+
+.B -theme <theme dir>
+     Specify the visual theme to use.
+
+.B +/-ia
+     When this option is enabled, AScd ignore the skip
+     orders found in WorkMan database and read all the
+     tracks of the CD.
+
+.B +/-up
+     When enabled, all the message (and the track title
+     display) are forced in uppercase.
+
+.B -showtitle
+     Show the track title if found in WorkMan database.
+     If the message panel is not large enough, AScd
+     scrolls the title.
+
+.B -showartist
+     Add the artist name to the track title (-showtitle
+     option needed)
+
+.B -debug
+     AScd verbose mode
+
+.B -save
+     Save all the settings and exit. Usefull to 'make' a
+     new configuration file: set all the stuff you want
+     to set and put -save as last commandline option. 
+     Example:
+
+     ascd +w +a -device /dev/cd0c -save
+
+     Then, you just call ascd without arguments, it will
+     use the settings you just saved.
+
+.SH FILES
+
+\fBascd\fP stores its settings in ~/.ascdrc.
+
+The theme files are usually stored in /usr/local/share/AScd.
+
+.SH INVOCATION
+\fBascd\fP can be called from one of many ways.  The most common invocation
+is the command line:
+.nf
+
+	% ascd -device /dev/wcd0a +w +a &
+
+.fi
+With Afterstep, another way to call \fBascd\fP is from the window
+manager:
+.nf
+
+	*Wharf - - Swallow "ascd" ascd&
+
+.fi
+This line, when placed in the \fI.steprc\fP file in the users home directory
+will cause \fBascd\fP to use the shape extensions so that it will be a
+button on the \fBWharf (1)\fP button bar under the \fBafterstep (1)\fP
+window manager.
+
+.SH COPYRIGHTS
+
+Copyright 1999, Rob Malda and Denis Bourez. No guarantees or
+warranties or anything are provided or implied in any way whatsoever.
+Use this program at your own risk. Permission to use this program for
+any purpose is given, as long as the copyright is kept intact.
+
+.SH AUTHORS
+
+Denis Bourez <[email protected]>
+     application since 0.7
+.br
+Rob Malda <[email protected]>
+     application until 0.6
+.br
+Dirk Foersterling <[email protected]>
+     WorkMan maintainer
+.br
+Steven Grimm <[email protected]>
+     WorkMan maintainer until 1.3
+.br
+Erik O'Shaughnessy <[email protected]>
+     AIX platform device driver
+.br
+Michael Kurz <[email protected]>
+     changes and man page until 0.6
+
+.SH MORE INFORMATION
+
+<URL:http://worldserver.oleane.com/rsn/>
+.br
+<URL:http://CmdrTaco.net/>
diff --git a/ascd/ascd/cdcontrol.c b/ascd/ascd/cdcontrol.c
new file mode 100644
index 0000000..b8daeff
--- /dev/null
+++ b/ascd/ascd/cdcontrol.c
@@ -0,0 +1,362 @@
+/* ===========================================================================
+ * AScd: cdcontrol.c
+ * This is cd_control() function: accept orders from the ui player and send 
+ * the appropriate commands to libworkman.
+ *
+ * Please do *not* modify this function as it is used without changes by
+ * several programs!
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+ 								   
+#define CD_C_VERSION "1.4"
+#include "ext.h"
+
+void 
+cd_control(int order)
+{
+    static int pos_changed = FALSE;
+    int currenttrack;
+
+    anomalie = 0;
+    wanna_play = FALSE;
+
+#ifdef XFASCD
+    update_xpm = TRUE;
+#endif
+
+    if (cur_cdmode != WM_CDM_EJECTED) {
+	if (cd->trk[cur_track - 1].data) datatrack = 1;
+	else datatrack = 0;
+    }
+
+    if ((cur_ntracks == 1) && (cd->trk[cur_track - 1].data)) {
+
+	/* only one track, and it's a data track. We ignore 
+	   all commands to avoid some funny things. Only the EJECT
+	   commands are allowed */
+
+	if ((order != STOP) && (order != EJECT)) {
+	    anomalie = 1;
+	    return;
+	}
+    }
+    
+    switch(order) {
+
+    case PLAY:
+	loop_mode = 0;
+	intro_mode = 0;
+	if (! pos_changed) {
+	    wm_cd_status();
+	} else {
+	    pos_changed = FALSE;
+	}
+
+	if (cur_track < 1) cur_track = 1;
+
+	/* don't play data tracks: */
+          
+	if (! cd->trk[cur_track - 1].data) {
+	    if (cur_cdmode != WM_CDM_EJECTED) {
+		if (cur_cdmode != WM_CDM_PAUSED) {
+		    if (do_autorepeat) {
+			cur_track = 1;
+			do_autorepeat = FALSE;
+		    }
+
+		    /* skip data and avoid tracks */
+		    while (((cd->trk[cur_track - 1].data) || ((cd->trk[cur_track - 1].avoid == 1) && (!ignore_avoid))         ) && (cur_track <= cur_ntracks)) cur_track++;
+
+		    if (cur_track > cur_ntracks) cur_track--;
+		    if (!(cd->trk[cur_track - 1].data)) {
+			wanna_play = TRUE;
+			wm_cd_play(cur_track, 0, cur_ntracks + 1);
+		    }
+		}
+	    }
+	}
+	break;
+
+    case PAUSE:
+	loop_mode = 0;
+	intro_mode = 0;
+
+	if (pos_changed) {
+	    cur_track = wanted_track;
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	    cur_cdmode = WM_CDM_PLAYING;
+	} else {
+	    wm_cd_pause();
+	}
+
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    pos_changed = FALSE;
+	}
+	break;
+
+    case STOP: /* Civilized handler: STOP and EJECT combined */
+	loop_mode = 0;
+	intro_mode = 0;
+	if ((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) {
+	    cur_cdmode = WM_CDM_PAUSED;
+	    wm_cd_stop();
+	    cur_cdmode = WM_CDM_STOPPED;
+	} else {
+	    if (cur_cdmode != WM_CDM_EJECTED) {
+		currenttrack = 0;
+		wm_cd_eject();
+	    }
+	}
+	pos_changed = FALSE;
+	break;
+
+    case STOPONLY: /* STOP, no more. Doesn't eject if CD is already stopped */
+	loop_mode = 0;
+	intro_mode = 0;
+	if ((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) {
+	    cur_cdmode = WM_CDM_PAUSED;
+	    wm_cd_stop();
+	    cur_cdmode = WM_CDM_STOPPED;
+	}
+	pos_changed = FALSE;
+	break;
+
+    case EJECT: /* Troll handler: don't try to guess the current mode, just eject */
+	if (cur_cdmode != WM_CDM_EJECTED) {
+	    loop_mode = 0;
+	    intro_mode = 0;
+	    currenttrack = 0;
+	    wm_cd_eject();
+	}
+	break;
+
+    case CLOSETRAY: /* let's try this */
+	if (cur_cdmode == WM_CDM_EJECTED) {
+	    /* commented, as current libworkman crashes when it receive this order */
+	    /*wm_cd_closetray();*/
+	}
+	break;
+
+    case UPTRACK: /* next track */
+	loop_mode = 0;
+	intro_mode = 0;
+	if (pos_changed) cur_track = wanted_track;
+
+	if (cur_track + 1 > cur_ntracks ) {
+	    cur_track = 1;
+	} else {
+	    cur_track ++;
+	}
+	if (cur_cdmode == WM_CDM_PAUSED) {
+	    pos_changed = TRUE;
+	    wanted_track = cur_track;
+	}
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	}
+	break;
+
+    case DNTRACK: /* previous track */
+	loop_mode = 0;
+	intro_mode = 0;
+	if (pos_changed) cur_track = wanted_track;
+
+	if (cur_cdmode == WM_CDM_PAUSED) {
+	    if (cur_pos_rel < 2 || pos_changed) cur_track--;
+	    cur_pos_rel = 0;
+	    pos_changed = TRUE;
+	} else if (cur_cdmode == WM_CDM_PLAYING) {
+	    if (cur_pos_rel < 2) cur_track--;
+	    cur_pos_rel = 0;
+	} else {
+	    cur_track--;
+	}
+	if (cur_track < 1) cur_track = cur_ntracks;
+	if (cur_cdmode == WM_CDM_PAUSED) {
+	    pos_changed = TRUE;
+	    wanted_track = cur_track;
+	}
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	}
+	break;
+
+    case DIRECTTRACK: /* direct access to a random track */
+	loop_mode = 0;
+	intro_mode = 0;
+
+	if (direct_track < 1) direct_track = 1;
+
+	if (cur_cdmode == WM_CDM_PAUSED) pos_changed = TRUE;
+	cur_track = direct_track;
+	if (cur_track >= cur_ntracks + 1) cur_track = 1;
+
+	/* if it's a data track, skip it */
+	while ((cd->trk[cur_track - 1].data) && (cur_track <= cur_ntracks)) cur_track++;
+          
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	}
+	break;
+
+    case CUE:
+	loop_mode = 0;
+	intro_mode = 0;
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, cur_pos_rel + cue_time, cur_ntracks + 1);
+	}
+	break;
+
+    case REV:
+	loop_mode = 0;
+	intro_mode = 0;
+	if ( (cur_cdmode == WM_CDM_PLAYING) && ( (cur_pos_rel - cue_time) >= 0 ) ) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, cur_pos_rel - cue_time, cur_ntracks + 1);
+	}
+	break;
+
+    case FIRST:
+	loop_mode = 0;
+	intro_mode = 0;
+	cur_track = 1;
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	} else if (cur_cdmode == WM_CDM_PAUSED) {
+	    pos_changed = TRUE;
+	    wanted_track = cur_track;
+	}
+	break;
+
+    case LAST:
+	loop_mode = 0;
+	intro_mode = 0;
+	cur_track = cur_ntracks;
+	if (cur_cdmode == WM_CDM_PLAYING) {
+	    wanna_play = TRUE;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	} else if (cur_cdmode == WM_CDM_PAUSED) {
+	    pos_changed = TRUE;
+	    wanted_track = cur_track;
+	}
+	break;
+
+    case LOOP:
+	if ( (loop_2 > loop_1) ||
+	     (loop_end_track > loop_start_track) ) {
+               
+	    if (loop_start_track == 0) loop_start_track = 1;
+	    intro_mode = 0;
+	    if ( ( loop_2 > loop_1 ) 
+		 || ( loop_start_track != loop_end_track ) )
+		currenttrack = loop_start_track;
+	    else {
+		loop_end_track = cur_ntracks;
+		currenttrack = loop_start_track = 1;
+		loop_1 = 0;
+		loop_2 = 
+		    thiscd.trk[ loop_end_track - 1 ].length - 1;
+	    }
+	    wm_cd_play(loop_start_track, loop_1, cur_ntracks + 1);
+	    wanna_play = TRUE;
+	    loop_mode = 1;
+	} else {
+	    anomalie = 1;
+	}
+	break;
+
+    case DIRECTACCESS: /* direct access to a random position of the current track */
+	if (direct_access < 0) direct_access = 0;
+	loop_mode = 0;
+	intro_mode = 0;
+	wm_cd_status();
+	wm_cd_play(cur_track, direct_access, cur_ntracks + 1);
+	wanna_play = TRUE;
+	break;
+
+    case GLOBALACCESS:
+	if (direct_access < 0) direct_access = 0;
+	loop_mode = 0;
+	intro_mode = 0;
+	wm_cd_status();
+	wm_cd_play(1, direct_access, cur_ntracks + 1);
+	wanna_play = TRUE;
+	break;
+
+    case INTROSCAN:
+	if (! intro_mode) {
+	    intro_mode = 1;
+	    wm_cd_play(cur_track, 0, cur_ntracks + 1);
+	} else {
+	    intro_mode = 0;
+	}
+	break;
+          
+    case INTRONEXT:
+	wm_cd_pause();
+	currenttrack = cur_track;
+	currenttrack++;
+	if (cur_track +1 > cur_ntracks ) currenttrack = 1;
+	else currenttrack = cur_track + 1;
+	cur_pos_rel = 0;
+	wm_cd_play(currenttrack, 0, cur_ntracks + 1);
+	break;
+          
+    case LOCACCESS:
+	if ( (loop_1 > 0) && (loop_start_track > 0) ) {
+	    intro_mode = 0;
+	    loop_mode = 0;
+	    currenttrack = loop_start_track;
+	    wm_cd_stop();
+	    wm_cd_play(loop_start_track, loop_1, cur_ntracks + 1);
+	    wanna_play = TRUE;
+	} else {
+	    anomalie = 1;
+	}
+	break;
+    }
+}
+
+char *cd_control_version(void)
+{
+        char *s = NULL;
+
+        wm_strmcat(&s, CD_C_VERSION);
+        return s;
+}
diff --git a/ascd/ascd/config.h b/ascd/ascd/config.h
new file mode 100644
index 0000000..67f2f44
--- /dev/null
+++ b/ascd/ascd/config.h
@@ -0,0 +1,6 @@
+#define PACKAGE "ascd"
+#define DEFAULT_COLOR "#2FAFAF"
+#define DEFAULT_BGCOLOR "#000000"
+#define DEFAULTDEVICE "/dev/cdrom"
+#define THDIR "/usr/local/share/AScd"
+#define VERSION 		"0.13.2"
diff --git a/ascd/ascd/configure b/ascd/ascd/configure
new file mode 100755
index 0000000..fe032bc
--- /dev/null
+++ b/ascd/ascd/configure
@@ -0,0 +1,203 @@
+#!/bin/sh
+
+LS=/bin/ls
+
+rm -f config.h
+rm -f dirs.h
+
+echo "/* auto-generated config.h for AScd. */" >  config.h
+echo "/* do not edit. Run ./configure!       */" >> config.h
+echo "" >> config.h
+
+echo "/* internal defines, do not change! */" >> config.h
+echo "#define PACKAGE \"ascd\"" >> config.h
+cat version.h >> config.h
+echo "" >> config.h
+echo "/* user choices */" >> config.h
+echo "#define DEFAULT_COLOR \"#2FAFAF\"" >> config.h
+echo "#define DEFAULT_BGCOLOR \"#000000\"" >> config.h
+
+clear
+
+#################################################################
+
+echo "Welcome to the ascd configuration script. Please answer the"
+echo "following questions before compilation."
+echo
+echo "--------------------"
+echo "1. Default CD device"
+echo "--------------------"
+echo
+echo "Please select a default CD device. This can be overriden at run"
+echo "time with the -d option. Default is /dev/cdrom. You can also type"
+echo "'NO' if you don't want to specify a default device (in case you're"
+echo "using Volume Manager for example."
+echo
+printf "Default device or NO (or return to accept the default): "
+read CDD
+echo
+
+case $CDD in
+   "")	echo "#define DEFAULTDEVICE \"/dev/cdrom\"" >> config.h
+	echo "** the default device will be /dev/cdrom"
+	;;
+   NO)	echo "#define DEFAULTDEVICE \"/dev/cdrom\"" >> config.h
+	echo "#define NO_D_DEVICE" >> config.h
+	echo "** no device option enabled"
+	;;
+   *)	echo "#define DEFAULTDEVICE \"$CDD\"" >> config.h
+	echo "** the default device will be $CDD"
+	;;
+esac
+
+#################################################################
+
+echo
+echo "------------------"
+echo "2. Binary location"
+echo "------------------"
+echo
+echo "Where shall I install ascd program? Press return to accept the"
+echo "default value (/usr/local/bin)."
+echo
+printf "Bin directory? "
+read BINDIR
+echo
+
+case $BINDIR in 
+    "") echo "#define CBINDIR /usr/local/bin" > dirs.h
+        echo "** Ascd will be /usr/local/bin/ascd"
+	;;
+    *)	echo "#define CBINDIR $BINDIR" >> dirs.h
+        echo "** Ascd will be $BINDIR/ascd"
+	;;
+esac
+
+#################################################################
+
+echo
+echo "---------------"
+echo "3. Man location"
+echo "---------------"
+echo
+echo "Where shall I install the man page? Press return to accept the"
+echo "default value (/usr/local/man/man1)."
+echo
+printf "Man directory? "
+read MANDIR
+echo 
+
+case $MANDIR in 
+    "") echo "#define CMANDIR /usr/local/man/man1" >> dirs.h
+        echo "** Man page will be installed in /usr/local/man/man1."
+	;;
+    *)	echo "#define CMANDIR $MANDIR" >> dirs.h
+        echo "** Man page will be installed in $MANDIR."
+	;;
+esac
+
+#################################################################
+
+echo
+echo "------------------"
+echo "4. themes location"
+echo "------------------"
+echo
+echo "Where shall I install the ascd themes files? Press return"
+echo "to accept default value (/usr/local/share/AScd)."
+echo
+printf "Themes pixmaps directory? "
+read THDIR
+echo
+
+case $THDIR in 
+    "") echo "#define THDIR \"/usr/local/share/AScd\"" >> config.h
+        echo "#define CTHDIR /usr/local/share/AScd" >> dirs.h
+        echo "** Theme dir: /usr/local/share/AScd."
+	;;
+    *)	echo "#define THDIR \"$THDIR\"" >> config.h
+        echo "#define CTHDIR $THDIR" >> dirs.h
+        echo "** Theme dir: $THDIR."
+	;;
+esac
+
+
+#################################################################
+
+echo
+echo "-------------------------"
+echo "5. optional WINGs support"
+echo "-------------------------"
+echo
+echo "If you're using Window Maker window manager, AScd can be compiled"
+echo "with a configuration window using WINGs toolkit. Please note that"
+echo "it's work in progress and you may have to change a few things in"
+echo "ascd/Imakefile."
+echo
+echo "1) compile with WINGs support"
+echo "2) no WINGs support (DEFAULT)"
+echo
+printf "Type 1 or 2: "
+read WINGS
+echo 
+
+case $WINGS in 
+    1)  echo "#define WMK" >> config.h
+    	echo "** Compiling with WINGs support."
+	;;
+    *)	echo "** No WINGs support."
+	;;
+esac
+
+#################################################################
+
+echo
+echo "-------------------------"
+echo "6. optional mixer support"
+echo "-------------------------"
+echo
+echo "This optional module adds mixing capabilities to AScd's themes."
+echo "It is not supported on a lot of platforms, and you may have to"
+echo "make some changes in the sources."
+echo
+echo "1) compile with mixer support"
+echo "2) no mixer support (DEFAULT)"
+echo
+printf "Type 1 or 2: "
+read WINGS
+echo
+
+case $WINGS in 
+    1)  echo "#define MIXER" >> config.h
+    	echo "** Compiling with mixer support."
+	;;
+    *)	echo "** No mixer support."
+	;;
+esac
+
+
+#################################################################
+
+echo
+echo "-----------------------------"
+echo "Automatic Makefile generation"
+echo "-----------------------------"
+echo
+echo "Configuration is done. This script is now running xmkmf to"
+echo "generate the Makefiles."
+echo
+
+xmkmf -a
+
+echo
+echo "---------"
+echo "Finished!"
+echo "---------"
+echo
+echo "You are now ready to compile AScd. type:"
+echo
+echo "make                ... to compile the software"
+echo "make install        ... to install it"
+echo "make install.man    ... to install the man page"
+echo
+echo
diff --git a/ascd/ascd/dirs.h b/ascd/ascd/dirs.h
new file mode 100644
index 0000000..6f1c2f9
--- /dev/null
+++ b/ascd/ascd/dirs.h
@@ -0,0 +1,3 @@
+#define CBINDIR /usr/local/bin
+#define CMANDIR /usr/local/man/man1
+#define CTHDIR /usr/local/share/AScd
diff --git a/ascd/ascd/doc/CHANGES b/ascd/ascd/doc/CHANGES
new file mode 100644
index 0000000..e114e93
--- /dev/null
+++ b/ascd/ascd/doc/CHANGES
@@ -0,0 +1,597 @@
+=====================================================================
+                            AScd Change Log
+=====================================================================
+
+------------- 09/05/99:
+
+- updated to latest libworkman snapshot (1.4.2, 99/06/20)
+- Themes doc updated
+
+------------- 07/05/99:
+
+- added panel_stop and panel_play options to Theme file format.
+
+------------- 06/22/99:
+
+- [fix] the track number is no longer drawn on every panel.
+- [fix] the mute function called the fade-in one!
+
+------------- 06/16/99:
+
+- updated to latest libworkman snapshot (1.4.2, 99/05/29)
+
+------------- 06/03/99:
+
+- [fix] small redraw bugs that could cause "flashy" screen updates
+- [fix] fade in/out mode was broken in previous 0.13.X.
+
+------------- 05/25/99:
+
+- new pixmap sliders. Currently only direct_access and volume are
+  implemented, and only horizontal ones.
+
+------------- 05/24/99:
+
+- [fix] th_icon_window was not correctly set in fak_init_theme()
+
+------------- 05/19/99:
+
+- [fix] %s instead of %d in -debug theme summary
+- added a -debug level
+
+------------- 05/19/99: 0.13.1
+
+- code cleanup
+- a few changes in Imakefile
+- more messages in -debug mode
+- a couple of fixes
+
+------------- 05/17/99: 0.13 *** beta stage ***
+
+- [fix] horizontal volume bar bug
+
+------------- 05/16/99:
+
+- added "quick ref" support with quick_ref command and a button
+  in the WINGs main window
+
+------------- 05/16/99: 
+
+- added support for vertical sliders (CD direct access, volume and
+  mixer) via vprogress_bar, vvolume_bar and vmixer_bar.
+- added support for inverted vertical sliders via iprogress_bar,
+  ivolume_bar and imixer_bar.
+- mixer support (FreeBSD and Linux only. Please send me a mail if 
+  you compile mixer support on other platforms.)
+- added -mixer commandline option to specify the mixer device
+- themes manual updated
+- man page updated
+- reorganized the WINGs src files
+
+------------- 05/13/99:
+
+- [fix] Track name was not shown for track #1
+
+------------- 05/11/99:
+
+- better database support: CD default volume and CD autoplay are
+  handled (CD autoplay if for WorkMan, AScd ignores this parameter)
+- better WINGs handling. The WINGs window is no longer closed by
+  the program when CD is ejected.
+- [fix] WINGs windows crashed when CD was stopped
+- [fix] big bug in database code that oftent crashed the 
+  program at startup time (cd_curtrack = -1)
+- [fix] bug in the song title scrolling (hardcoded length)
+
+------------- 05/11/99: 0.13pr6
+
+- small updates in the man page (so outdated...)
+- the theme manual (in PostScript) is now in the archive and 
+  installed in /usr/local/share/AScd
+- added alt pixmap support for eject command
+- added database panel which can now be different from message one
+- changed the themes loading mechanism: themes are now installed in
+  /usr/local/share/AScd/Themes. There's also a Default/ dir that can
+  contain pixmaps. If a pixmap is not found in the theme dir, AScd
+  also search in the Default dir. By default, this dir contains 
+  digits.xpm and digits2.xpm.
+- the theme selectors now use dirent instead of the '.dir' file
+- added alt pixmap support for mute command
+
+------------- 05/10/99: 0.13pr5
+
+- [fix] Less "floating point errors" should happen (I hope)
+- [fix] the "°" in the counter was not drawn at the right place
+- added icon keyword for a premilinary separate window/icon window
+  handling. See sample themes!
+- added no_icon_window and icon_window prologues keywords to use
+  with big window themes (that cannot fit in the icon window)
+- added volume slider handle with "volume" command
+- [fix] bug in direct access bar handling
+
+------------- 05/10/99: 0.13pr4
+
+- [fix] string truncate when its length > message panel width
+- added comment keyword in Theme files
+
+------------- 05/09/99: 0.13pr3
+
+New features and fixes since 0.12.1:
+
+- |fix] ./configure didn't work when using an alt pixmap path
+- [fix] a STOP order is sent to drive at the CD end (!autorepeat)
+- changed the global redraw time offsets
+- support for 8bit fonts
+- added a line if WINGs "about" window
+- added autoprobe mode, activated by default (+/-p cl option)
+- [fix] crash at CD end on Linux systems
+
+0.13 fixes and additions:
+
+- added a second debug level
+- the redraw routine has been redesigned. Screen updates should be
+  less 'flashy' now...!
+
+------------- 05/08/99: 0.13pr2
+
+New features and fixes since 0.12.1:
+
+- a few arrangements in the WINGs windows layout
+- added a timeout for the theme and fast track selectors
+- the fast track selector now scrolls the song titles
+- support for loop clear and loop CD from start to current track
+- mut be linked to 990505 LibWorkMan snapshot
+- [fix] cd_control updated to 1.4 (added security in direct track)
+- [fix] there was a bug in the cd_volume() calls in mute feature
+- [fix] the messages now have priority on track title scrolling
+
+0.13 fixes and additions:
+
+- the buttons containing no command are no longuer parsed in 
+  case of mouse events
+- support for introscan
+- support for fade in/out
+- support for theme selector
+- support for fast track selector
+- support for mute
+- WINGs module works again, but the visual theme selector crashes :(
+
+------------- 05/07/99: 0.13pr1 **** BACK TO ALPHA STAGE ****
+
+It's the biggest change since I begun working on ascd. Everything 
+that appears on the screen is now 100% configurable. Put the buttons
+you want, where you want!
+
+- major rewrite of AScd. The source code has been splitted in 
+  several smaller C files.
+- [bug] the WINGs support shall *not* compile. I didn't have time
+  to update this code with the new features.
+- added -debug cl option
+
+All of the CD player commands are not yet available. Check the next
+pre-releases.
+
+------------- 05/06/99:
+
+- [fix] redraw problem at CD's end when autorepeat is on.
+- [fix] redraw problem when selecting "PLAY" with a stopped CD.
+
+=====================================================================
+
+------------- 05/02/99: 0.12.1 
+
+- [fix WINGs] the themes popup didn't display the selected theme
+- multiple-mask shape mode, to allow transparent background with
+  non rectangular buttons
+- added a theme selector in the main window, activated by right
+  clicking on the message line
+- [fix] when a theme is not found, ascd try to use the default one
+  instead of crashing.
+
+------------- 04/19/99: 0.12.0 RELEASE
+
+- added runtime visual themes management, with -theme cl option
+- added a popup menu in configuration window to change the visual
+  theme *without* restarting the program
+- [fix] there was a bug in the mouse routine that could eject CD 
+  if the mouse click was outside the buttons
+- [fix] a couple of typos in the WINGs windows.
+
+=====================================================================
+
+------------- 04/17/99: 0.11.2
+
+- the volume is no longer set to the maximum at startup. AScd now
+  uses libworkman to read the initial volume.
+- [fix] clicking on "play" when CD was ejected crashed the program.
+- [fix] changed the WMCreateButton calls (didn't compile with rh 5.2)
+- added an #undef OSS_SUPPORT in libworkman's plat_linux.c
+- again, small changes in libworkman's Config
+
+------------- 04/15/99: "Le Chien"
+
+- added "+/-ia" CL option to ignore avoid tags in WorkMan's database
+- added a button in WINGs window to control this parameter
+- cdcontrol updated to 1.3 release, with a few cleanups
+- added "+/-up" CL option to force uppercase messages
+- added a button in WINGs window to control this parameter
+- removed the "counter" popup, replaced by radio buttons
+
+------------- 04/14/99: 0.11.1
+
+- added lowercase letters in (r)digits.xpm. I had to make a few 
+  changes in the background pixmap to display the new letters. I
+  also added a conversion that remove accents.
+- added 2 buttons in WINGs window to switch on/off song name and
+  artist scrolling in main window.
+- added a field to edit the cd device in WINGs window
+- [fix] with a songname <= 8 cars, the artist was not shown 
+- [fix?] autorepeat
+
+------------- 04/13/99: 0.11.0 RELEASE
+
+- song title (and CD artist) display with scrolling
+- added -showtitle and -showartist command line options
+- in ejected mode, the "eject" button is replaced by the "quit" one
+- [fix] redraw problems with panel switching when CD is ejected
+- [cdcontrol] fix with track n# after eject/load
+- AScd now supports WorkMan's "avoid track" parameter. It has a better
+  handling than XfAScd: try it with Prince's "New Power Soul", when you
+  have to skip tracks 11 to 48...
+- the database edition fixed (didn't save!!!)
+
+=====================================================================
+
+------------- 04/12/99:
+
+- added the "fast track selector" by clicking on the track number
+  when playing. Click play button to play selected track.
+- cdcontrol updated to version 1.2, fixing a bug in autoplay.
+- [fix] event handling should be smoother (especialy mouse clicks)
+
+------------- 04/10/99: "Human Being"
+
+Well... Once again, the screen layout was redesigned. And the mixer
+is gone again!
+
+------------- 03/30/99:
+
+- added -xf command line option
+- first attempt to add database editing. Ugly!
+
+------------- 03/19/99: (known as 0.10.1 RELEASE)
+
+- added misc/default script to generate a default config.h file
+  without asking any question.
+
+=====================================================================
+
+------------- 03/17/99: 0.10.0 RELEASE
+
+- switched to current LibWorkMan, which is no longuer included in
+  the archive.
+- added a lot of settings in the WINGs window
+- added a configure script
+- updated cdcontrol to 1.0pre1
+- changed the pixmaps selection
+- code cleanup
+- [fix] foreground color saving was buggy!
+- [fix] the muted volume was not saved
+- [fix] crashed at startup with no cd (WINGs option only)
+
+=====================================================================
+
+------------- 03/16/99: 0.9.4
+
+- added the optionnal WINGs window with WorkMan database display,
+  volume setting and autoplay/repeat switches. Still very
+  experimental!
+- the timer handling was redesigned and should be more efficient.
+- added fade in/out functions (right button on play)
+- better volume control, the min/max/current volume can be loaded and
+  saved (~/.ascdrc)
+- "-save" commandline option
+
+------------- 03/01/99:
+
+- "blind mode": in this mode, once a CD is started, AScd never
+  updates the counter and never check the drive status.
+- changed the idle timeout to 500 ms (as in future 1.0.)
+
+------------- 02/26/99: 0.9.3
+
+- [fix] autoplay fixes ("-1" track number at startup) (1.0 tree fix)
+- [fix] pressing "eject" when the CD was already ejected crashed the
+  program (1.0 tree fix)
+
+------------- 02/10/99: 0.9.2 "p control"
+
+* All:
+
+- changed a few things in the Imakefiles. The "Config" file is now
+  replaced by config.h, wish should be a little more simplier
+- [fix] a few typos in docs, man page and program.
+
+* Ascd:
+
+- added the -mdevice command line option to specify the mixer device.
+- the mixer_device is now saved/loaded in ~/.ascdrc
+- [fix] if LEDs colors where defined in ~/.ascd, the commandline
+  -bg and -fg didn't work -> "not enough color cells"
+- [fix] when paused, selecting first or last track didn't work
+- in mixer mode, the "track nbr" now displays the value for the
+  current mixer slider
+- added an alternate pixmap set, "fusion", which can be set in the
+  config.h file.
+- man page updated
+
+------------- 01/28/99: 0.9.1 "Late at night"
+
+* Ascd:
+
+- [fix] 0.9 didn't compile if NO_D_DEVICE was defined. Thanks to
+  Gus Hartmann who found the bug.
+- [fix] 0.9 entered a crazy loop at CD end when autorepeat 
+  was off.
+
+------------- 01/27/99: 0.9 RELEASE
+
+* Ascd:
+
+- code cleanup.
+
+------------- 01/26/99: PR5 "If U need a fix..."
+
+* Ascd:
+
+- [fix] ascd crashed on launch with no cd in tray (since 01/22)
+- [fix] up/down buttons didn't work when paused (since 01/22)
+- [fix] track nbr was not properly redrawn when using up/down 
+  buttons when stopped (since 01/21)
+- [fix] window was not properly redrawn when stopped (since 0.8-R)
+- [fix] it was not possible to access the last track when stopped
+- [fix] autorepeat works again (and fastly than in 0.8)
+- the time display mode is now saved in ~/.ascdrc
+
+* Lib:
+
+- [BIG FIX] ascd now ejects CDs under FreeBSD. And there should be
+  no more device locking (it was necessary to quit ascd to eject
+  the CD and/or to mount a cdrom). Think this works with NetBSD too.
+  (bug found in 0.4+, 0.6+, 0.7 & 0.8)
+
+------------- 01/25/99: PR4 "If U need a mix..."
+
+* Ascd:
+
+- 100% new mixer section, with access to all the 17 mixing "sliders" 
+  of OSS.
+- the back and foreground colors may now be different for the CD 
+  player and the mixer.
+- all the mixer settings can be saved in a ~/.ascdrc file. This file
+  can be loaded at startup.
+- Total rewrite of the command line parse function. Added several 
+  new commands. See ascd -help for further details.
+
+* Lib:
+
+- [fix] I forgot to add the plat_openbsd.c in the Imakefile!
+- added an option in the Config file to compile libworkman as a
+  shared lib. Tested with FreeBSD 2.2.
+
+------------- 01/22/99: PR3 "The Bug Factory"
+
+* Ascd:
+
+- Almost total rewrite of cd_control() function. The code should be
+  more reliable (I hope!). Some clean-ups two.
+- no more uses old ascd variables (currenttrack, maxtracks, playing, 
+  paused). Only the WorkMan ones (cur_track, cur_ntracks, cur_cdmode)
+  are used.
+- on launch, if autoplay is disabled, the track nbr panel displays
+  how tracks are on the CD instead of "01".
+- when the time panel displays the global remaining time of the CD, 
+  the track nbr panel displays the number of remaining tracks.
+- new alpha pixmaps.
+- adjusted timing for checking inserted CD in autoplay mode.
+- Middle click on the display switch the mute mode. Muted/Unmuted 
+  volumes can be set with 'm' and 'v' command line parameters.
+- [fix] in autoplay mode, launching ascd with an inserted audio CD in
+  drive didn't play this one automatically. 
+
+------------- 01/21/99: PR2
+
+* Ascd:
+
+- The mixer code was removed once again, due to the big XPM code 
+  changes.
+- XPM code redesigned: each button has its own XPM file.
+
+------------- 01/14/99:
+
+* Lib:
+
+- added O_RDONLY | O_NONBLOCK flagsto the cd_device opening function
+  in plat_linux.c and plat_freebsd.c. Thanks to Jens Axboe 
+  ([email protected])
+  
+------------- 12/20/98: PR1
+
+* Lib:
+
+- using WorkMan 1.3b (BETA) code instead of WorkMan 1.3a
+- patched plat_freebsd.c to compile under FreeBSD 3.0. Thanks to 
+  Stephane Marzloff ([email protected])
+- plat_openbsd.c added (WorkMan)
+
+=====================================================================
+
+------------- 12/18/98: 0.8 RELEASE
+
+* Lib:
+
+- added plat_aix.c in lib dir. Thanks to Erik O'Shaughnessy
+  ([email protected]) who wrote this driver and sent it to me.
+
+* Ascd:
+
+- Pixmaps are back to 48x48 pixel size
+- Small fixes in buttons pixmaps ; new digits pixmaps
+- right click on STOP button kills app.
+- mixer section is back again!
+- man page updated.
+
+------------- 12/07/98:
+
+* Lib:
+
+- srcs moved into subdirs, as PR1->PR4.
+- Imakefiles are now working on Solaris. Makefile.solaris removed.
+- WorkMan code is compiled as a library.
+
+* Ascd:
+
+- ascd now works with Solaris 7 i386 (gcc).
+
+------------- 11/29/98:
+
+- lib should now compile on Solaris 7 with gcc, using a specific
+  Makefile. 
+- added NO_D_DEVICE option for Solaris Volume Manager.
+
+------------- 12/01/97:
+
+* Lib:
+
+- [fix] it was impossible to play an inserted CD if autoplay mode was
+  disabled.
+
+------------- 09/25/97 -> 11/25/97:
+
+* Lib:
+
+- 'Config' file where all compilation options can be changed.
+  can be changed.
+- now recognize WorkMan splited tracks. Try it with Prince's 
+  Lovesexy's album !!
+
+* Ascd:
+
+- ascd now includes asmixer!
+- added a "mixer screen" based on asmixer 0.5: clic on 
+  time display with right button to switch to it.
+- added -v, -1, -2, -3 command line parameters for the
+  mixer screen (same as asmixer). Removed the old -v option.
+- setvol() and setvol_command() from asmixer src merged.
+- added -t command line parameter: autorepeat
+- stopped CD's display CD Length
+- added "-b" command line option to specify the LEDs
+  background color.
+- changed "-l" option to "-f" ([F]oreground color)
+- when CD is ejected, track nbr shows "--".
+- all the XPMs are now included in ascd.c, to allow to
+  define the bg color. I also fixed a few things in the 
+  "old and quick made" ascd+ buttons.
+- when global time is displayed, clicking on the
+  progress bar give access to the whole disk.
+- added a "#define VERSION" in ascd.c
+- added the new options (since 0.7) in the man page 
+
+=====================================================================
+
+09/04/97: 0.7 release
+
+(denis) based on 0.4+, but with the WindowMaker specific code taken
+from ascd 0.6
+
+- added: "-v" command line option to specify the volume to set
+  when right-clicking on the time display.
+- added: the time display now show a "-" before the remaining time(s)
+  and a "o" if it displays the CD ellapsed/remaining time.
+- added: folding tags in the ascd.c for the jed editor
+- added: "-l" command line option to change the LEDs colour
+- added: clicking on the "DOWN" button restart the track ; another
+  click plays the previous track, like on a diskman (from Rob's
+  ascd 0.6)
+- fixed: redisplay of the XPMs was buggy with 0.4+ (but worked with
+  afterstep... this fix is primary for WM)
+- fixed: when autoplay is off, ascd doesn't scan the CD drive until
+  you click on a button. No more thousands of "cannot read audio disk"
+  in the FreeBSD logs :) (but there's still the problem with the "-a"
+  option)
+
+=====================================================================
+
+v0.6
+- Most important: WindowMaker Dock support with the -w flag!
+   
+=====================================================================
+
+03/25/97: 0.4+ release
+
+(denis) this is a bug-fixes release.
+
+- fix: when first launch and if there was no CD in drive, a click on
+  the play button crashed the program.
+- fixed a "floating point exception" when the track progress bar is
+  clicked on the very top.
+- changed the "0.4" to "0.4+" for the "-h" option.
+
+=====================================================================
+
+03/23-24/97: 0.4+ release
+
+(denis) Based on Rob Malda's ascd 0.4. All the Workbone Code has been
+removed. I use the Workman source code: ascd can be compiled on
+several systems with several kinds of CD players, but tests have been
+made only on FreeBSD 2.2.X (i386) and Linux 2.X (i386) with IDE
+drives. Should work with SCSI drives too. (note: I removed the Workman
+database code)
+
+- Imakefile has been updated for the Workman code.
+- added: several new features (loops, introscan, progress bar...)
+  (see the README file)
+- changed the XPM files for the new buttons and the progress bar.
+- added: 4th digit for songs longer than 10 minutes.
+- added: -a command line option. Activates the autoplay mode: ascd
+  automatically plays the CD when it is inserted.
+- added: -c command line option which sets the value, in seconds, 
+  for the intro scan mode and the cue/review function.
+- changed the indentation in the source code with the auto-
+  indentation in jed. Sorry ;-)
+- when a DATA CDROM is inserted, the display now show "--:--".
+- a few fixes in ascd.c
+- the default device is now /dev/cdrom.
+
+=====================================================================
+
+v0.4 - 1.15.96
+- Thanks olly again (Does this guy sleep?) We have a few
+  very nice bug fixes, and a nice new feature.
+- ascd now stops correctly at the end of a CD.
+- 'Next Track' used to go to far
+- When ascd is paused, the LED now blinks!
+- v0.4's secret code word is LLAMA BAIT
+
+=====================================================================
+
+v0.3 - 1.13.96
+- Thanks to [email protected], if a CD is playing when
+  ascd launches, it detects this and starts following it.
+- Thanks to the 9,000 of you who e-mailed me fixes (thanks
+  guys, its nice to know people use this crap!) a Data CD
+  will not crash ascd.
+- About 15 people sent in CHEEZ DOODLE's to me.  Thanks.
+  The code word for v0.3 is ORANGE NIPPLE.
+
+=====================================================================
+
+v0.2
+- Fixed Certain Neat Error Messages
+- I Think it should now get the last track from CD's.
+- It shouldn't *lock* the CD Player from other devices
+  once you press the 'stop' button.  This is especially
+  useful with ascdc.
+- Now includes the correct README file.  Thanks to the
+  Hundreds of you who caught that.  It's nice to know
+  someone reads these files.    
diff --git a/ascd/ascd/doc/NEWS b/ascd/ascd/doc/NEWS
new file mode 100644
index 0000000..8ae59a9
--- /dev/null
+++ b/ascd/ascd/doc/NEWS
@@ -0,0 +1,148 @@
+New features in ascd 0.13-beta:
+===============================
+
+(it is a big big big release, as I had to redesign or rewrite nearly
+everything in the soft except the CD control functions. So, I'm
+considering this code as beta stage one.)
+
+* Visual Themes:
+
+  Visual themes are now 100% configurable. A theme consist in a description 
+  file and various pixmaps. You can place any screen element anywhere in the 
+  window and the icon. Available screen elements:
+
+  - pixmaps
+  - counter
+  - track number
+  - message area
+  - database display area
+  - direct access horizontal, vertical and inverted vertical bars
+  - hardware volume horizontal, vertical and inverted vertical bars
+  - mixer control horizontal, vertical and inverted vertical bars
+
+  For each element, you specify the *command* to use for the three mouse
+  buttons. AScd provides commands to control the general options, the CD
+  player and the mixer.
+
+  AScd is no longer restricted to a "dockable" size. More: In -w mode, it's 
+  now possible to have different screen layouts for the main window and the 
+  icon one
+
+* Mixer support is back (FreeBSD and Linux are the only supported platforms)
+* New documentation (PostScript) about making new themes
+* LibWorkman updated to 990505, with early CDDB support (but it is *not*
+  yet supported by AScd)
+* database support has been enhanced
+* Quick Reference gif files support with an external viewer (xv by default)
+* the font used in messages and database is now 8bit compatible, thanks to
+  Stefan Zeiger who made an excellent 8bit new font
+* added -debug support, which could help when making/debugging new themes
+* Speed improvment for a few features (fast track selector for instance)
+* Better messages hierarchy
+* CD control updated to 1.4 with a few fixes
+* ascd.c has been splitted in several modules
+
+New features in ascd 0.12:
+==========================
+
+* visual theme support!!! With the WINGs configuration
+  window, it is possible to switch between themes without
+  restarting ascd.
+
+New features in ascd 0.11.X:
+============================
+
+* another brand new look, with a lot of enhancements
+* the mixer was removed once again
+* better volume handling
+* size set to 56x56 as it's ok with AfterStep's Wharf
+* CD control updated to 1.2 with several fixes
+* database editing (ALPHA!) in optional WINGs module
+* AScd can show and scroll CD artist and track title
+* a few commandline options to control this feature
+* Added a commandline option to launch Xfascd
+* The "avoid track" WorkMan's parameter is handled
+* fast track access when playing by left/right clicking
+  on the track number
+* a lot of enhancements in the configuration window
+* fixes, as usual
+
+New features in ascd 0.10:
+==========================
+
+* Using LibWorkMan 99/03/07
+* New configure script
+* CD control updated to 1.0pre1
+* Enhanced WINGs module
+* fixes
+
+0.9.4:
+======
+
+* optionnal config window using WINGs (WindowMaker) toolkit.
+* fade in/out
+* blind mode
+* volume settings are now saved in ~/.ascdrc
+* -save commandline option
+
+New features in ascd 0.9.1,2 and 3:
+===================================
+
+* 0.9.3 is a bug-fixes only
+* fixes, fixes and fixes!
+* alternate pixmaps set which be set in the config file
+* mixer device can be changed with a command line option and
+  is stored in ~/.ascdrc
+* in mixer mode, the track nbr displays the current mixer
+  setting.
+* full mixer section added to man page.
+
+New features in ascd 0.9:
+=========================
+
+* Lib code updated to WorkMan 1.3b with:
+  - plat_linux.c patched for glib systems
+  - plat_openbsd.c added
+* Added an option in Config to compile libworkman as a
+  shared library (this works with ascd 0.8 too)
+* plat_freebsd.c patched for FreeBSD 3.0-STABLE and hacked:
+  Eject now works and there're no more device locking!
+* REWRITE of the pixmap routines: there are now one pixmap
+  per button, and several pixmaps are new.
+* REWRITE of the CD control routine, which is now in a
+  separate src file.
+* REWRITE from scratch of the mixer.
+* REWRITE of the command line parsing function. Added a few
+  new options. See the man page or type ascd -help.
+* ascd can save and load its settings in ~/.ascdrc file. It
+  is also possible to save the 17 mixer settings in this file. The 
+  .ascdrc settings can be overidden with the commandline options.
+* added drive volume control with a "mute" switch (values
+  can be changed with commandline options). Note that mute
+  function controls the CD Drive Volume, and is independant 
+  from the mixer module.
+* The LED colors can be changed for the mixer (Config file, 
+  commandline & ~/.ascdrc)
+* Autoplay and autorepeat modes redesigned. They should ok now.
+  (still a few little problems with autoplay detecting)
+* a lot of fixes, including redraw problems found in 0.7 & 0.8.
+
+New features in ascd 0.8:
+=========================
+
+* 'Config' file to specify compilation time default values
+* Hardware code (WorkMan) updated and compiled as a lib
+* Compilation on Solaris 7 i386 with gcc
+* Compilation on AIX (Thanks to Erik O'Shaughnessy)
+* Optional Mixer module (similar to asmixer)
+* New pixmaps, size changed from 56x56 to 48x48
+* Text messages displaying mode changes
+* LEDs colors can be changed from the commandline
+* several fixes
+* updated man page
+* french translation for docs
+* direct access to every part of the CD with a single click!
+* right clic on "stop" button end the application
+
+==========================================================================
+See CHANGES for details. Denis.
diff --git a/ascd/ascd/ext.h b/ascd/ascd/ext.h
new file mode 100644
index 0000000..cdf84a6
--- /dev/null
+++ b/ascd/ascd/ext.h
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include <X11/Xlib.h>
+#include <X11/xpm.h>
+#include <X11/extensions/shape.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>
+#include <dirent.h>
+
+#include "config.h"
+
+#include "ascd.h"
+#include <workman/workman.h>
+
+#ifdef WMK
+#include <WINGs.h>
+#endif
+
+extern open();
+extern time();
+
+/* added this for this lib: */
+extern unsigned int info_modified;
+
+extern int wanted_track;
+extern unsigned int do_autorepeat;
+
+extern int anomalie;
+
+/* xfascd vars */
+
+extern unsigned int wanna_play;
+
+#ifdef XFASCD
+extern unsigned int update_xpm;
+#endif
+extern unsigned int blind_mode;
+extern unsigned int loop_mode, intro_mode;
+extern unsigned int datatrack, cue_time;
+extern unsigned int loop_start_track, loop_end_track;
+extern unsigned int loop_1;
+extern unsigned int loop_2;
+extern unsigned int direct_access, direct_track;
+extern unsigned int ignore_avoid;
+
+extern int lasttime;
+
+extern Display *Disp;
+extern Window Root;
+extern Window Iconwin;
+extern Window Win;
+extern char *Geometry;
+extern char device[];
+extern char xv[];
+extern int withdrawn;
+extern GC WinGC;
+extern int CarrierOn;
+
+extern char led_text[];
+
+extern unsigned int text_timeout;
+extern long text_start;
+extern int redraw;
+extern int mixer_changed;
+
+extern unsigned int autorepeat;
+extern unsigned int autoplay;
+extern unsigned int autoprobe;
+extern unsigned int time_mode;
+extern unsigned int global_mode;
+
+extern unsigned int volume;
+extern  unsigned int muted_volume;
+extern unsigned int muted;
+extern unsigned int unmuted_volume;
+
+extern unsigned int fade_ok;
+
+extern unsigned int old_track;
+
+extern int selectors_timeout;
+
+/* The WorkMan stuff: */
+
+extern char *cd_device;
+
+/* from/for WorkMan database code: */
+
+extern int mark_a;
+extern int mark_b;
+extern int cur_stopmode;
+extern int cur_playnew;
+
+extern char theme[];
+extern char selected_theme[];
+extern unsigned int theme_select;
+extern unsigned int theme_select_nbr;
+extern int no_mask;
+extern unsigned int show_db;
+extern unsigned int show_artist;
+extern unsigned int show_db_pos;
+extern unsigned int show_icon_db_pos;
+extern unsigned int force_upper;
+extern unsigned int fast_track;
+extern unsigned int xflaunch;
+extern unsigned int fade_out;
+extern unsigned int fade_step;
+
+extern int min_volume,max_volume;  /* from LibWorkMan */
+
+extern int debug;
+
+extern unsigned int panel;           /* current panel */
+extern unsigned int panels;          /* how many panels? */
+extern unsigned int but_max;         /* how many buttons? */
+extern unsigned int but_msg;         /* which one is the message zone? */
+extern unsigned int but_counter;     /* which one is the counter? */
+extern unsigned int but_tracknbr;    /* which one is the track number? */
+extern unsigned int but_db;
+extern unsigned int icon_msg;
+extern unsigned int icon_counter;
+extern unsigned int icon_tracknbr;
+extern unsigned int but_current;
+
+extern unsigned int panel_stop;
+extern unsigned int panel_play;
+
+extern char th_name[];
+extern char th_author[];
+extern char th_release[];
+extern char th_email[];
+extern char th_url[];
+extern char th_comment[];
+extern char th_alpha1[];
+extern char th_alpha2[];
+extern char th_background[];
+extern char th_icon_window[];
+
+extern int th_no_minus;
+extern int th_no_icon_window;
+
+extern struct fak_button thdata[];
+
+extern XpmIcon alphaXPM;
+extern XpmIcon ralphaXPM;
+extern XpmIcon backXPM;
+extern XpmIcon iconXPM;
+
+#ifdef WMK
+#include "wings_ext.h"
+#endif
+
+#ifdef MIXER
+#include "mixer_ext.h"
+#endif
diff --git a/ascd/ascd/faktory.c b/ascd/ascd/faktory.c
new file mode 100644
index 0000000..79973e3
--- /dev/null
+++ b/ascd/ascd/faktory.c
@@ -0,0 +1,1777 @@
+/* ===========================================================================
+ * AScd: faktory.c
+ * The theme handling functions
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+ 								   
+#include "ext.h"
+
+#define MAX_LINE_NAME 16
+
+void tes_sncpy(char *out, char *in, int n)
+{
+    register int i;
+    for (i=0; i<=n-1; i++) out[i] = in[i];
+    out[i] = '\0';
+}
+
+char *tes_xgets(char *chaine, int nb, FILE *fichier)
+{
+    char *r;
+    r = fgets(chaine, nb, fichier);
+    if (feof(fichier) == 0) chaine[strlen(chaine) - 1] = 0;
+    return r;
+}
+
+int fak_parse_line(char *ligne, char *key, char *arguments)
+{
+    unsigned int pos = 0;
+
+    if ((strlen(ligne) > 0) && (ligne[0] != '#')) {
+	while ((ligne[pos] != ' ') && (ligne[pos] != 9)) pos++;
+	tes_sncpy(key, ligne, pos);
+	while (((ligne[pos] == ' ') || (ligne[pos] == 9)) && (pos < strlen(ligne))) pos++;
+	if (pos < strlen(ligne)) strcpy(arguments, ligne + pos);
+	else strcpy(arguments, "");
+	if (debug > 2) fprintf(stderr,"++ input: [%s]\n   key:   [%s]\n   args:  [%s]\n", ligne, key, arguments);
+	return TRUE;
+    } else {
+	strcpy(key, "");
+	strcpy(arguments, "");
+	if (debug > 2) fprintf(stderr,"++ input: [%s]\n   key:   [%s]\n   args:  [%s]\n", ligne, key, arguments);
+	return FALSE;
+    }
+}
+
+int fak_use_alt(int i)
+{
+    int use_alt = 0;
+    switch (thdata[i].left) {
+    case FAK_CD_PLAY:
+	if (cur_cdmode == WM_CDM_PLAYING) use_alt = 1;
+	break;
+    case FAK_CD_PAUSE:
+	if (cur_cdmode == WM_CDM_PAUSED) use_alt = 1;
+	break;
+    case FAK_CD_STOP:
+	if (cur_cdmode == WM_CDM_STOPPED) use_alt = 1;
+	break;
+    case FAK_CD_EJECT:
+	if (cur_cdmode == WM_CDM_EJECTED) use_alt = 1;
+	break;
+    case FAK_CD_STOPEJECT:
+	if (cur_cdmode == WM_CDM_STOPPED) use_alt = 1;
+	break;
+    case FAK_CD_EJECTQUIT:
+	if ((cur_cdmode == WM_CDM_EJECTED) || (cur_cdmode == WM_CDM_STOPPED)) use_alt = 1;
+	break;
+    case FAK_CD_LOOP:
+	if (loop_mode) use_alt = 1;
+	break;
+    case FAK_CD_MUTE:
+	if (muted) use_alt = 1;
+	break;
+    case FAK_CD_VOLUME:
+	if (muted) use_alt = 1;
+	break;
+    case FAK_CD_INTRO:
+	if (intro_mode) use_alt = 1;
+	break;
+    case FAK_TOG_AUTOREPEAT:
+	if (autorepeat) use_alt = 1;
+	break;
+    case FAK_TOG_AUTOPLAY:
+	if (autoplay) use_alt = 1;
+	break;
+    case FAK_TOG_UPPER:
+	if (force_upper) use_alt = 1;
+	break;
+    case FAK_TOG_SHOWDB:
+	if (show_db) use_alt = 1;
+	break;
+    case FAK_TOG_SHOWARTIST:
+	if (show_artist) use_alt = 1;
+	break;
+    case FAK_TOG_ISKIPS:
+	if (ignore_avoid) use_alt = 1;
+	break;
+    case FAK_TSELECT:
+    case FAK_TNEXT:
+    case FAK_TPREVIOUS:
+	if (theme_select) use_alt = 1;
+	break;
+    default:
+	use_alt = 0;
+	break;
+    }
+    return use_alt;
+}
+
+void fak_validate_pixmap(char *string, char *name)
+{
+    sprintf(string, "%s/Themes/%s/%s", THDIR, theme, name);
+    if (access(string, R_OK) != 0) {
+	sprintf(string, "%s/Default/%s", THDIR, name);
+	if (access(string, R_OK) != 0) {
+	    fprintf(stderr, "\nascd: fatal error while loading theme '%s'\n", theme);
+	    fprintf(stderr, "      Pixmap file '%s' was not found.\n\n", name);
+	    exit(1);
+	}
+    }
+}
+
+void fak_init_theme(int upgrade) 
+{
+    /* re-init all the buttons structure and globals */
+    
+    int i;
+
+    if (debug) fprintf(stderr, "** Init theme variables.\n   Buttons...\n");
+    for (i = 1; i <= but_max ; i++) {
+
+	thdata[i].type = 0;
+	thdata[i].panel = 0;
+	thdata[i].left = 0;
+	thdata[i].mid = 0;
+	thdata[i].right = 0;	
+	thdata[i].x = 0;
+	thdata[i].y = 0;
+	thdata[i].w = 0;
+	thdata[i].h = 0;
+	thdata[i].arg = 0;
+	thdata[i].icon = FALSE;
+	thdata[i].ox = 0;
+	thdata[i].oy = 0;
+
+	if (strcmp(thdata[i].xpm_file, "") != 0) {
+	    strcpy(thdata[i].xpm_file, "");
+	    /*if (upgrade) XpmFree(&thdata[i].xpm);*/
+	}
+	if (strcmp(thdata[i].altxpm_file, "") != 0) {
+	    strcpy(thdata[i].altxpm_file, "");
+	    /*if (upgrade) XpmFree(&thdata[i].altxpm);*/
+	}
+    }
+
+    if (debug) fprintf(stderr, "   Strings & globals...\n");
+
+    strcpy(th_name, "");
+    strcpy(th_author, "");
+    strcpy(th_release, "");
+    strcpy(th_email, "");
+    strcpy(th_url, "");
+    strcpy(th_comment, "");
+    strcpy(th_alpha1, "");
+    strcpy(th_alpha2, "");
+    strcpy(th_background, "");
+    strcpy(th_icon_window, "");
+
+    th_no_minus = FALSE;
+
+    but_max = 0;
+    but_counter = 0;
+    but_msg = 0;
+    but_tracknbr = 0;
+    but_db = 0;
+    but_current = 0;
+
+    icon_counter = 0;
+    icon_msg = 0;
+    icon_tracknbr = 0;
+
+    panels = 0;
+    panel = 1;
+
+    panel_stop = 0;
+    panel_play = 0;
+
+    /*
+    if (upgrade) {
+	XpmFree(&alphaXPM);
+	XpmFree(&ralphaXPM);
+	XpmFree(&backXPM);
+    }
+    */
+    if (debug) fprintf(stderr, "-> done.\n");
+
+}
+
+int fak_load_theme(char *th, int upgrade) 
+{
+    char txt[256];
+    char key[256];
+    char arguments[256];
+    int button = FALSE;
+    unsigned int i;
+    XWindowAttributes Attributes;
+    int Ret;
+    FILE *in;
+#ifdef MIXER
+    unsigned int j;
+struct {
+    char name[MAX_LINE_NAME];
+    int dev;
+} mixernames[SOUND_MIXER_NRDEVICES] = {
+    { "volume", SOUND_MIXER_VOLUME },
+    { "bass", SOUND_MIXER_BASS },
+    { "treble", SOUND_MIXER_TREBLE },
+    { "midi", SOUND_MIXER_SYNTH },
+    { "pcm", SOUND_MIXER_PCM },
+    { "speaker", SOUND_MIXER_SPEAKER },
+    { "line", SOUND_MIXER_LINE },
+    { "mic", SOUND_MIXER_MIC },
+    { "cd", SOUND_MIXER_CD },
+    { "imix", SOUND_MIXER_IMIX },
+    { "altpcm", SOUND_MIXER_ALTPCM },
+    { "reclev", SOUND_MIXER_RECLEV },
+    { "igain", SOUND_MIXER_IGAIN },
+    { "ogain", SOUND_MIXER_OGAIN },
+    { "line1", SOUND_MIXER_LINE1 },
+    { "line2", SOUND_MIXER_LINE2 },
+    { "line3", SOUND_MIXER_LINE3 }
+};
+#endif
+
+    /* check if theme directory if ok: */
+
+    sprintf(txt, "%s/Themes/%s", THDIR, th);
+
+    if (debug) fprintf(stderr, "** Theme to load: %s\n", txt);
+
+    if (access(txt, X_OK | R_OK) != 0) {
+	sprintf(txt, "%s/Themes/default", THDIR);
+	if (access(txt, X_OK | R_OK) != 0) {
+	    fprintf(stderr, "ascd: fatal error\n\n");
+	    fprintf(stderr, "The '%s' and 'default' themes folders are not \navailable in %s.\n\n", th, THDIR);
+	    fprintf(stderr, "Please check the permissions and/or redo ascd installation.\n\n");
+	    exit(1);
+	} else {
+	    strcpy(th, "default");
+	}
+    }
+
+    sprintf(txt, "%s/Themes/%s/Theme", THDIR, th);
+
+    /* parse and load the file */
+
+    if (in = fopen(txt, "r")) {
+
+	/* we first clear everything... */
+
+	fak_init_theme(upgrade);
+
+	/* that's ok, let's go! */
+
+	if (debug) fprintf(stderr, "-> Parsing Theme file...\n");
+	while (tes_xgets(txt, 255, in)) {
+	    if (fak_parse_line(txt, key, arguments)) {
+		if (key[0] == '{') {
+		    button = TRUE;
+		    if (but_max < FAK_BMAX - 1) {
+			but_max ++;
+			strcpy(thdata[but_max].xpm_file, "");
+			strcpy(thdata[but_max].altxpm_file, "");
+			thdata[but_max].type = 0;
+		    } else {
+			/* no more free buttons. We'll ignore the rest */
+			button = FALSE;
+		    }
+		} else if (key[0] == '}') {
+		    button = FALSE;
+		    
+		    /* update the pointers to msg, counter and tracknbr: */
+
+		    switch(thdata[but_max].type) {
+		    case FAK_COUNTER:
+			if (thdata[but_max].icon) icon_counter = but_max;
+			else but_counter = but_max;
+			break;
+		    case FAK_MSG:
+			if (thdata[but_max].icon) icon_msg = but_max;
+			else but_msg = but_max;
+			break;
+		    case FAK_DB:
+			but_db = but_max;
+			break;
+		    case FAK_TRACKNBR:
+			if (thdata[but_max].icon) icon_tracknbr = but_max;
+			else but_tracknbr = but_max;
+			break;
+		    default:
+			break;
+		    }
+
+		    if (debug > 1) {
+			fprintf(stderr, "   Button %02d type: %02d commands: %02d %02d %02d arg: %02d\n", but_max, thdata[but_max].type, thdata[but_max].left, thdata[but_max].mid, thdata[but_max].right, thdata[but_max].arg);
+			fprintf(stderr, "   Geometry: %02dx%02d on panel %02d\n", thdata[but_max].x, thdata[but_max].y, thdata[but_max].panel);
+			fprintf(stderr, "   Pixmaps: [%s] and [%s]\n", thdata[but_max].xpm_file, thdata[but_max].altxpm_file);
+		    }
+		}
+
+		if (!button) {
+		    if (strcmp(key, "name") == 0) strcpy(th_name, arguments);
+		    else if (strcmp(key, "release") == 0) strcpy(th_release, arguments);
+		    else if (strcmp(key, "author") == 0) strcpy(th_author, arguments);
+		    else if (strcmp(key, "email") == 0) strcpy(th_email, arguments);
+		    else if (strcmp(key, "url") == 0) strcpy(th_url, arguments);
+		    else if (strcmp(key, "comment") == 0) strcpy(th_comment, arguments);
+		    else if (strcmp(key, "alpha1") == 0) strcpy(th_alpha1, arguments);
+		    else if (strcmp(key, "alpha2") == 0) strcpy(th_alpha2, arguments);
+		    else if (strcmp(key, "background") == 0) strcpy(th_background, arguments);
+		    else if (strcmp(key, "icon_background") == 0) strcpy(th_icon_window, arguments);
+		    else if (strcmp(key, "panels") == 0) panels = atoi(arguments);
+		    else if (strcmp(key, "panel_stop") == 0) panel_stop = atoi(arguments);
+		    else if (strcmp(key, "panel_play") == 0) panel_play = atoi(arguments);
+		    else if (strcmp(key, "no_minus") == 0) th_no_minus = TRUE;
+		} else {
+
+		    /* button definition: */
+
+		    if (strcmp(key, "icon") == 0) thdata[but_max].icon = TRUE;
+
+		    if (strcmp(key, "type") == 0) {
+			if (strcmp(arguments, "pixmap") == 0) thdata[but_max].type = FAK_PIXMAP;
+			else if (strcmp(arguments, "counter") == 0) thdata[but_max].type = FAK_COUNTER;
+			else if (strcmp(arguments, "tracknumber") == 0) thdata[but_max].type = FAK_TRACKNBR;
+			else if (strcmp(arguments, "message") == 0) thdata[but_max].type = FAK_MSG;
+			else if (strcmp(arguments, "database") == 0) thdata[but_max].type = FAK_DB;
+			else if (strcmp(arguments, "progress_bar") == 0) thdata[but_max].type = FAK_CD_BAR;
+			else if (strcmp(arguments, "vprogress_bar") == 0) thdata[but_max].type = FAK_VCD_BAR;
+			else if (strcmp(arguments, "iprogress_bar") == 0) thdata[but_max].type = FAK_ICD_BAR;
+			else if (strcmp(arguments, "pixmap_bar") == 0) thdata[but_max].type = FAK_CD_PIX;
+			else if (strcmp(arguments, "vpixmap_bar") == 0) thdata[but_max].type = FAK_VCD_PIX;
+			else if (strcmp(arguments, "volume_bar") == 0) thdata[but_max].type = FAK_VOL_BAR;
+			else if (strcmp(arguments, "volume") == 0) thdata[but_max].type = FAK_VOL_BAR;
+			else if (strcmp(arguments, "pixmap_volume") == 0) thdata[but_max].type = FAK_VOL_PIX;
+			else if (strcmp(arguments, "vpixmap_volume") == 0) thdata[but_max].type = FAK_VVOL_PIX;
+			else if (strcmp(arguments, "vvolume") == 0) thdata[but_max].type = FAK_VVOL_BAR;
+			else if (strcmp(arguments, "vvolume_bar") == 0) thdata[but_max].type = FAK_VVOL_BAR;
+			else if (strcmp(arguments, "ivolume") == 0) thdata[but_max].type = FAK_IVOL_BAR;
+			else if (strcmp(arguments, "ivolume_bar") == 0) thdata[but_max].type = FAK_IVOL_BAR;
+			else if (strcmp(arguments, "mixer_bar") == 0) thdata[but_max].type = FAK_MIXER_BAR;
+			else if (strcmp(arguments, "mixer") == 0) thdata[but_max].type = FAK_MIXER_BAR;
+			else if (strcmp(arguments, "vmixer_bar") == 0) thdata[but_max].type = FAK_VMIXER_BAR;
+			else if (strcmp(arguments, "vmixer") == 0) thdata[but_max].type = FAK_VMIXER_BAR;
+			else if (strcmp(arguments, "imixer_bar") == 0) thdata[but_max].type = FAK_IMIXER_BAR;
+			else if (strcmp(arguments, "imixer") == 0) thdata[but_max].type = FAK_IMIXER_BAR;
+		    }
+
+		    i = 0;
+		    if ((strcmp(key, "left") == 0) || 
+			(strcmp(key, "middle") ==0) || 
+			(strcmp(key, "mid") ==0) || 
+			(strcmp(key, "right") == 0)) {
+
+			/* --------------- available commands: ---------------- */
+
+			/* basic orders */
+
+			if (strcmp(arguments, "panel_switch") == 0) i = FAK_PANEL_SWITCH;
+			else if (strcmp(arguments, "panel 1") == 0) i = FAK_PANEL1;
+			else if (strcmp(arguments, "panel 2") == 0) i = FAK_PANEL2;
+			else if (strcmp(arguments, "panel 3") == 0) i = FAK_PANEL3;
+			else if (strcmp(arguments, "panel 4") == 0) i = FAK_PANEL4;
+			else if (strcmp(arguments, "panel 5") == 0) i = FAK_PANEL5;
+			else if (strcmp(arguments, "counter_mode") == 0) i = FAK_COUNTER_MODE;
+			else if (strcmp(arguments, "quit") == 0) i = FAK_QUIT;
+			else if (strcmp(arguments, "wings_window") == 0) i = FAK_WINGS;
+			else if (strcmp(arguments, "theme_select") == 0) i = FAK_TSELECT;
+			else if (strcmp(arguments, "theme_next") == 0) i = FAK_TNEXT;
+			else if (strcmp(arguments, "theme_previous") == 0) i = FAK_TPREVIOUS;
+			else if (strcmp(arguments, "ftrack_select") == 0) i = FAK_FTSELECT;
+			else if (strcmp(arguments, "ftrack_next") == 0) i = FAK_FTNEXT;
+			else if (strcmp(arguments, "ftrack_previous") == 0) i = FAK_FTPREVIOUS;
+			else if (strcmp(arguments, "autoplay") == 0) i = FAK_TOG_AUTOPLAY;
+			else if (strcmp(arguments, "autorepeat") == 0) i = FAK_TOG_AUTOREPEAT;
+			else if (strcmp(arguments, "show_db") == 0) i = FAK_TOG_SHOWDB;
+			else if (strcmp(arguments, "show_artist") == 0) i = FAK_TOG_SHOWARTIST;
+			else if (strcmp(arguments, "uppercase") == 0) i = FAK_TOG_UPPER;
+			else if (strcmp(arguments, "ignore_skips") == 0) i = FAK_TOG_ISKIPS;
+			else if (strcmp(arguments, "save") == 0) i = FAK_SAVE;
+			else if (strcmp(arguments, "load") == 0) i = FAK_LOAD;
+			else if (strcmp(arguments, "quick_ref") == 0) i = FAK_QREF;
+
+			/* CD player */
+
+			else if (strcmp(arguments, "play") == 0) i = FAK_CD_PLAY;
+			else if (strcmp(arguments, "pause") == 0) i = FAK_CD_PAUSE;
+			else if (strcmp(arguments, "stop") == 0) i = FAK_CD_STOP;
+			else if (strcmp(arguments, "eject") == 0) i = FAK_CD_EJECT;
+			else if (strcmp(arguments, "stop_eject") == 0) i = FAK_CD_STOPEJECT;
+			else if (strcmp(arguments, "eject_quit") == 0) i = FAK_CD_EJECTQUIT;
+			else if (strcmp(arguments, "rew") == 0) i = FAK_CD_REW;
+			else if (strcmp(arguments, "first_track") == 0) i = FAK_CD_FIRST;
+			else if (strcmp(arguments, "previous_track") == 0) i = FAK_CD_PREVIOUS;
+			else if (strcmp(arguments, "fwd") == 0) i = FAK_CD_FWD;
+			else if (strcmp(arguments, "last_track") == 0) i = FAK_CD_LAST;
+			else if (strcmp(arguments, "next_track") == 0) i = FAK_CD_NEXT;
+			else if (strcmp(arguments, "direct_access") == 0) i = FAK_CD_DIRECT;
+			else if (strcmp(arguments, "loop") == 0) i = FAK_CD_LOOP;
+			else if (strcmp(arguments, "loop_start") == 0) i = FAK_CD_LSTART;
+			else if (strcmp(arguments, "loop_end") == 0) i = FAK_CD_LEND;
+			else if (strcmp(arguments, "loop_go_start") == 0) i = FAK_CD_GOLSTART;
+			else if (strcmp(arguments, "loop_go_end") == 0) i = FAK_CD_GOLEND;
+			else if (strcmp(arguments, "loop_track") == 0) i = FAK_CD_LTRACK;
+			else if (strcmp(arguments, "loop_to_track") == 0) i = FAK_CD_LTOTRACK;
+			else if (strcmp(arguments, "loop_from_track") == 0) i = FAK_CD_LFROMTRACK;
+			else if (strcmp(arguments, "loop_clear") == 0) i = FAK_CD_LCLEAR;
+			else if (strcmp(arguments, "intro_scan") == 0) i = FAK_CD_INTRO;
+			else if (strcmp(arguments, "fade") == 0) i = FAK_CD_FADE;
+			else if (strcmp(arguments, "random_jump") == 0) i = FAK_CD_RANDOM;
+			else if (strcmp(arguments, "random_mode") == 0) i = FAK_CD_RMODE;
+			else if (strcmp(arguments, "mute") == 0) i = FAK_CD_MUTE;
+			else if (strcmp(arguments, "volume") == 0) i = FAK_CD_VOLUME;
+
+			/* mixer */
+
+			else if (strcmp(arguments, "set") == 0) i = FAK_MIXER_SET;
+			else if (strcmp(arguments, "zero") == 0) i = FAK_MIXER_0;
+			else if (strcmp(arguments, "0") == 0) i = FAK_MIXER_0;
+			else if (strcmp(arguments, "50%") == 0) i = FAK_MIXER_50;
+			else if (strcmp(arguments, "50") == 0) i = FAK_MIXER_50;
+			else if (strcmp(arguments, "75%") == 0) i = FAK_MIXER_75;
+			else if (strcmp(arguments, "75") == 0) i = FAK_MIXER_75;
+			else if (strcmp(arguments, "100%") == 0) i = FAK_MIXER_100;
+			else if (strcmp(arguments, "100") == 0) i = FAK_MIXER_100;
+
+			/* ------------- which mouse button? --------------- */
+
+			if (strcmp(key, "left") == 0) thdata[but_max].left = i;
+			else if (strcmp(key, "middle") == 0) thdata[but_max].mid = i;
+			else if (strcmp(key, "mid") == 0) thdata[but_max].mid = i;
+			else if (strcmp(key, "right") == 0) thdata[but_max].right = i;
+		    }
+
+		    else if (strcmp(key, "panel") == 0) thdata[but_max].panel = atoi(arguments);
+		    else if (strcmp(key, "x") == 0) thdata[but_max].x = atoi(arguments);
+		    else if (strcmp(key, "y") == 0) thdata[but_max].y = atoi(arguments);
+		    else if (strcmp(key, "w") == 0) thdata[but_max].w = atoi(arguments);
+		    else if (strcmp(key, "h") == 0) thdata[but_max].h = atoi(arguments);
+		    else if (strcmp(key, "arg") == 0) {
+			thdata[but_max].arg = atoi(arguments);
+#ifdef MIXER
+			for(j = 0; j < SOUND_MIXER_NRDEVICES; j++) {
+			    if(strcasecmp(mixernames[j].name, arguments) == 0) {
+				thdata[but_max].arg = mixernames[j].dev;
+				break;
+			    }
+			}
+#endif
+		    }
+		    else if (strcmp(key, "pixmap") == 0) strcpy(thdata[but_max].xpm_file, arguments);
+		    else if (strcmp(key, "alt") == 0) strcpy(thdata[but_max].altxpm_file, arguments);
+		}
+	    }
+	}
+	fclose(in);
+
+	if (debug) {
+	    fprintf(stderr, "-> Theme summary for '%s':\n", th_name);
+	    fprintf(stderr, "   Release: %s\n", th_release);
+	    fprintf(stderr, "   Author:  %s (%s)\n", th_author, th_email);
+	    fprintf(stderr, "   URL:     %s\n", th_url);
+	    fprintf(stderr, "   Comment: %s\n", th_comment);
+	    fprintf(stderr, "   Stats:   %d panels, %d buttons\n", panels, but_max);
+	    fprintf(stderr, "   Display: counter: %d / msg: %d (%d cars) / db: %d / track: %d\n", but_counter, but_msg, thdata[but_msg].w, but_db, but_tracknbr);
+	    if (th_no_icon_window) 	    fprintf(stderr, "   Using separate icon definition, background = %s\n", th_icon_window);
+	    fprintf(stderr, "-> Loading basic pixmap files... ");
+	}
+
+	XGetWindowAttributes(Disp, Root, &Attributes);
+
+	/* the background pixmap: */
+
+	if (debug) fprintf(stderr, "background ");
+
+	fak_validate_pixmap(txt, th_background);
+	Ret = XpmReadFileToPixmap(Disp, Root, txt, &backXPM.pixmap,
+				  &backXPM.mask, &backXPM.attributes);
+	if ((Ret != XpmSuccess) && (debug)) fprintf(stderr, "*!* ");
+
+	/* the fonts: */
+	
+	if (debug) fprintf(stderr, "alpha 1 ");
+
+	fak_validate_pixmap(txt, th_alpha1);
+	Ret = XpmReadFileToPixmap(Disp, Root, txt, &alphaXPM.pixmap,
+				  &alphaXPM.mask, &alphaXPM.attributes);
+	if ((Ret != XpmSuccess) && (debug)) fprintf(stderr, "*!* ");
+
+	if (debug) fprintf(stderr, "2 ");
+
+	fak_validate_pixmap(txt, th_alpha2);
+	Ret = XpmReadFileToPixmap(Disp, Root, txt, &ralphaXPM.pixmap,
+				  &ralphaXPM.mask, &ralphaXPM.attributes);
+	if ((Ret != XpmSuccess) && (debug)) fprintf(stderr, "*!* ");
+	
+	if (debug) fprintf(stderr, "\n-> Loading buttons pixmaps... ");
+	for (i = 1; i <= but_max; i++) {
+	    if (strlen(thdata[i].xpm_file) > 0) {
+		if (debug > 1) fprintf(stderr, "%d ", i);
+		fak_validate_pixmap(txt, thdata[i].xpm_file);
+		thdata[i].xpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
+		Ret = XpmReadFileToPixmap(Disp, Root, txt, &thdata[i].xpm.pixmap, &thdata[i].xpm.mask, &thdata[i].xpm.attributes);
+		if ((Ret != XpmSuccess) && (debug > 1)) fprintf(stderr, "*!* ");
+	    }
+	    if (strlen(thdata[i].altxpm_file) > 0) {
+		if (debug > 1) fprintf(stderr, "a ");
+		fak_validate_pixmap(txt, thdata[i].altxpm_file);
+		thdata[i].xpm.attributes.valuemask |= (XpmReturnPixels | XpmReturnExtensions);
+		Ret = XpmReadFileToPixmap(Disp, Root, txt, &thdata[i].altxpm.pixmap, &thdata[i].altxpm.mask, &thdata[i].altxpm.attributes);
+		if ((Ret != XpmSuccess) && (debug > 1)) fprintf(stderr, "*!* ");
+	    }
+	}
+
+	/* the icon */
+
+	if (strlen(th_icon_window) > 0) {
+	    if (debug) fprintf(stderr, "\n-> Loading icon... ");
+	    fak_validate_pixmap(txt, th_icon_window);
+	    Ret = XpmReadFileToPixmap(Disp, Root, txt, &iconXPM.pixmap,
+				      &iconXPM.mask, &iconXPM.attributes);
+	    if ((Ret != XpmSuccess) && (debug)) fprintf(stderr, "*!* ");
+	}
+
+	if (debug) fprintf(stderr, "\n-> Theme loaded.\n");
+	return TRUE;
+    } else {
+	return FALSE;
+    }
+}
+
+void fak_icon_text(char *string, unsigned int where, unsigned int col, unsigned int red) 
+{
+    unsigned int x = 0;
+    unsigned int offset = 0;
+    unsigned hoffset = 0;
+    unsigned voffset = 0;
+    unsigned char txt[128];
+    unsigned int j;
+    unsigned int char_max;
+    int out = FALSE;
+
+    if (!th_no_icon_window) return;
+
+    switch(where) {
+    case COUNTER_PANEL:
+	if (icon_counter == 0) out = TRUE;
+	else {
+	    voffset = thdata[icon_counter].y;
+	    hoffset = thdata[icon_counter].x;
+	}
+	break;
+    case MSG_PANEL:
+	if (icon_msg == 0) out = TRUE;
+	else {
+	    voffset = thdata[icon_msg].y;
+	    hoffset = thdata[icon_msg].x;
+	}
+	break;
+    default:
+	if (icon_tracknbr == 0) out = TRUE;
+	else {
+	    voffset = thdata[icon_tracknbr].y;
+	    hoffset = thdata[icon_tracknbr].x;
+	}
+	break;
+    }
+
+    if (out) return;
+
+    strcpy(txt, string);
+
+    /* Deal with the string length: */
+
+    if ((strlen(txt) > thdata[icon_msg].w) && (where == MSG_PANEL)) {
+	txt[thdata[icon_msg].w] = 0;
+    
+    }
+	
+    if ((strcmp(txt, "") == 0) && (where == MSG_PANEL)) {
+	for (x = 1; x <= thdata[icon_msg].w; x++) {
+	    strcat(txt, " ");
+	}
+    }
+ 
+
+    if (red) {
+	char_max = alphaXPM.attributes.width / 6;
+    } else {
+	char_max = ralphaXPM.attributes.width / 6;
+    }
+
+    for(x = 0; x < strlen(txt); x++) {
+
+	/* lookup in the accents table to remove them */
+	if (! force_upper) {
+
+	    if (char_max <= 116) { /* don't convert if 8bit font */
+		for (j = 0 ; j <= strlen(ACCTABLE) - 2 ; j = j + 2) {
+		    if ( txt[x] == (unsigned char)ACCTABLE[j] ) {
+			txt[x] = ACCTABLE[j + 1];
+			break;
+		    }
+		}
+	    }
+
+	} else {
+	    /* force upper case */
+	    for (j = 0 ; j <= strlen(UPACCTABLE) - 2 ; j = j + 2) {
+		if ( txt[x] == (unsigned char)UPACCTABLE[j] ) {
+		    txt[x] = UPACCTABLE[j + 1];
+		    break;
+		}
+	    }
+	    txt[x] = toupper(txt[x]);
+	}
+
+	if ((txt[x] >= 32) && (txt[x] <= char_max + 32)) offset = txt[x] - 32;
+	else offset = 31;
+
+	if (red) {
+	    XCopyArea(Disp, ralphaXPM.pixmap, Iconwin, WinGC,
+		      6 * offset, 0, 6, 9, 
+		      hoffset + (col * 6) + (x * 6), 
+		      voffset);
+	} else {
+	    XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+		      6 * offset, 0, 6, 9, 
+		      hoffset + (col * 6) + (x * 6), 
+		      voffset);
+	}
+	
+    }
+}
+
+void fak_text(char *string, unsigned int where, unsigned int col, unsigned int red) {
+
+    /* different behaviour than the old draw_text() found in AScd <= 0.12.... The
+       where parameter replaces the odl "row" one and should be:
+
+       0 = write to the counter zone
+       1 = write to the message zone
+       2 = write to the track number zone
+    */
+
+    unsigned int x = 0;
+    unsigned int offset = 0;
+    unsigned hoffset = 0;
+    unsigned voffset = 0;
+    unsigned char txt[128];
+    unsigned int j;
+    unsigned int char_max;
+    int out = FALSE;
+    int do_icon = TRUE;
+
+    if (where == DB_PANEL) {
+	do_icon = FALSE;
+	if (but_db == 0) where = MSG_PANEL;
+    }
+
+    if (do_icon) fak_icon_text(string, where, col, red);
+
+    switch(where) {
+    case COUNTER_PANEL:
+	if (but_counter == 0) out = TRUE;
+	else {
+	    voffset = thdata[but_counter].y;
+	    hoffset = thdata[but_counter].x;
+	}
+	break;
+    case MSG_PANEL:
+	if (but_msg == 0) out = TRUE;
+	else {
+	    voffset = thdata[but_msg].y;
+	    hoffset = thdata[but_msg].x;
+	}
+	break;
+    case DB_PANEL:
+	voffset = thdata[but_db].y;
+	hoffset = thdata[but_db].x;
+	break;
+    default:
+	if (but_tracknbr == 0) out = TRUE;
+	else {
+	    voffset = thdata[but_tracknbr].y;
+	    hoffset = thdata[but_tracknbr].x;
+	}
+	break;
+    }
+
+    if (out) return;
+
+    strcpy(txt, string);
+
+    /* Deal with the string length: */
+
+    if ((strlen(txt) > thdata[but_msg].w) && (where == MSG_PANEL)) {
+	txt[thdata[but_msg].w] = 0;
+    
+    }
+
+    if ((strlen(txt) > thdata[but_db].w) && (where == DB_PANEL)) {
+	txt[thdata[but_msg].w] = 0;
+    
+    }
+	
+    if ((strcmp(txt, "") == 0) && (where == MSG_PANEL)) {
+	for (x = 1; x <= thdata[but_msg].w; x++) {
+	    strcat(txt, " ");
+	}
+    }
+    
+    if ((strcmp(txt, "") == 0) && (where == DB_PANEL)) {
+	for (x = 1; x <= thdata[but_db].w; x++) {
+	    strcat(txt, " ");
+	}
+    }
+
+    if (red) {
+	char_max = alphaXPM.attributes.width / 6;
+    } else {
+	char_max = ralphaXPM.attributes.width / 6;
+    }
+
+    for(x = 0; x < strlen(txt); x++) {
+
+	/* lookup in the accents table to remove them */
+	if (! force_upper) {
+
+	    if (char_max <= 116) { /* don't convert if 8bit font */
+		for (j = 0 ; j <= strlen(ACCTABLE) - 2 ; j = j + 2) {
+		    if ( txt[x] == (unsigned char)ACCTABLE[j] ) {
+			txt[x] = ACCTABLE[j + 1];
+			break;
+		    }
+		}
+	    }
+
+	} else {
+	    /* force upper case */
+	    for (j = 0 ; j <= strlen(UPACCTABLE) - 2 ; j = j + 2) {
+		if ( txt[x] == (unsigned char)UPACCTABLE[j] ) {
+		    txt[x] = UPACCTABLE[j + 1];
+		    break;
+		}
+	    }
+	    txt[x] = toupper(txt[x]);
+	}
+
+	if ((txt[x] >= 32) && (txt[x] <= char_max + 32)) offset = txt[x] - 32;
+	else offset = 31;
+
+	if (red) {
+	    XCopyArea(Disp, ralphaXPM.pixmap, Win, WinGC,
+		      6 * offset, 0, 6, 9, 
+		      hoffset + (col * 6) + (x * 6), 
+		      voffset);
+
+	    if (!th_no_icon_window) {
+		XCopyArea(Disp, ralphaXPM.pixmap, Iconwin, WinGC,
+			  6 * offset, 0, 6, 9, 
+			  hoffset + (col * 6) + (x * 6), 
+		      voffset);
+	    }
+	} else {
+	    XCopyArea(Disp, alphaXPM.pixmap, Win, WinGC,
+		      6 * offset, 0, 6, 9, 
+		      hoffset + (col * 6) + (x * 6), 
+		      voffset);
+
+	    if (!th_no_icon_window) {
+		XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+			  6 * offset, 0, 6, 9, 
+			  hoffset + (col * 6) + (x * 6), 
+		      voffset);
+	    }
+	}
+	
+    }
+}
+
+int fak_flush_expose(Window w) 
+{
+    XEvent dummy;
+    int i=0;
+
+    while (XCheckTypedWindowEvent (Disp, w, Expose, &dummy))i++;
+    return i;
+} 
+
+void fak_minus(void) {
+    if (th_no_minus) return;
+
+    if (time_mode == 1) {
+	if (!th_no_icon_window) {
+	    if (but_counter == 0) return;
+	    XCopyArea(Disp, alphaXPM.pixmap, Win, WinGC,
+		      6 * (45 - 32), 0, 6, 7, 
+		      thdata[but_counter].x, thdata[but_counter].y);
+	    XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+		      6 * (45 - 32), 0, 6, 7, 
+		      thdata[but_counter].x, thdata[but_counter].y);
+	} else {
+	    if (but_counter > 0) {
+		XCopyArea(Disp, alphaXPM.pixmap, Win, WinGC,
+			  6 * (45 - 32), 0, 6, 7, 
+			  thdata[but_counter].x, thdata[but_counter].y);
+	    }
+	    if (icon_counter > 0) {
+		XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+			  6 * (45 - 32), 0, 6, 7, 
+			  thdata[icon_counter].x, thdata[icon_counter].y);
+	    }
+	}
+    } else if (time_mode == 3) {
+	if (!th_no_icon_window) {
+	    if (but_counter == 0) return;
+	    XCopyArea(Disp, alphaXPM.pixmap, Win, WinGC,
+		      588, 0, 4, 7, 
+		      thdata[but_counter].x, thdata[but_counter].y);
+	    XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+		      588, 0, 4, 7, 
+		      thdata[but_counter].x, thdata[but_counter].y);
+	} else {
+	    if (but_counter > 0) {
+		XCopyArea(Disp, alphaXPM.pixmap, Win, WinGC,
+			  588, 0, 4, 7, 
+			  thdata[but_counter].x, thdata[but_counter].y);
+	    }
+	    if (icon_counter > 0) {
+		XCopyArea(Disp, alphaXPM.pixmap, Iconwin, WinGC,
+			  588, 0, 4, 7, 
+			  thdata[icon_counter].x, thdata[icon_counter].y);
+	    }
+	}
+    }
+
+    /* if displaying CD times, we display a little 'cd' */
+
+    if (time_mode >= 2) {
+	if (!th_no_icon_window) {
+	    if (but_counter == 0) return;
+	    XCopyArea(Disp, ralphaXPM.pixmap, Win, WinGC,
+		      582, 0, 4, 4,
+		      thdata[but_counter].x, thdata[but_counter].y);
+	    XCopyArea(Disp, ralphaXPM.pixmap, Iconwin, WinGC,
+		      582, 0, 4, 4,
+		      thdata[but_counter].x, thdata[but_counter].y);
+	} else {
+	    if (but_counter > 0) {
+		XCopyArea(Disp, ralphaXPM.pixmap, Win, WinGC,
+			  582, 0, 4, 4,
+			  thdata[but_counter].x, thdata[but_counter].y);
+	    }
+	    if (icon_counter > 0) {
+		XCopyArea(Disp, ralphaXPM.pixmap, Iconwin, WinGC,
+			  582, 0, 4, 4,
+			  thdata[icon_counter].x, thdata[icon_counter].y);
+	    }
+	}	    
+    }
+
+}
+
+void fak_singlemask(int i)
+{
+    /* we first remove pixmap and alt from the mask */
+
+    if (strlen(thdata[i].xpm_file) > 0) {
+	if (!th_no_icon_window) {
+	    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].xpm.mask, ShapeSubtract);
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].xpm.mask, ShapeSubtract);
+	} else {
+	    if (thdata[i].icon) {
+		XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].xpm.mask, ShapeSubtract);
+	    } else {
+		XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].xpm.mask, ShapeSubtract);
+	    }
+	}
+    }
+
+    if (strlen(thdata[i].altxpm_file) > 0) {
+	if (!th_no_icon_window) {
+	    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].altxpm.mask, ShapeSubtract);
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].altxpm.mask, ShapeSubtract);
+	} else {
+	    if (thdata[i].icon) {
+		XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].altxpm.mask, ShapeSubtract);
+	    } else {
+		XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].altxpm.mask, ShapeSubtract);
+	    }
+	}
+    }
+
+    /* then we set of the two : */
+
+    if ((fak_use_alt(i)) && (strlen(thdata[i].altxpm_file) > 0)) {
+	if (!th_no_icon_window) {
+	    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].altxpm.mask, ShapeUnion);
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].altxpm.mask, ShapeUnion);
+	} else {
+	    if (thdata[i].icon) {
+		XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].altxpm.mask, ShapeUnion);
+	    } else {
+		XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].altxpm.mask, ShapeUnion);
+	    }
+	}
+    } else {
+	if (!th_no_icon_window) {
+	    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].xpm.mask, ShapeUnion);
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].xpm.mask, ShapeUnion);
+	} else {
+	    if (thdata[i].icon) {
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+			      thdata[i].xpm.mask, ShapeUnion);
+	    } else {
+		XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+				  thdata[i].xpm.mask, ShapeUnion);
+	    }
+	}
+    }
+}
+
+void fak_maskset()
+{
+    int i;
+
+    if (debug) fprintf(stderr, "** Setting pixmaps mask\n");
+    if (debug) fprintf(stderr, "-> background\n");
+
+    XShapeCombineMask(Disp, Win, ShapeBounding, 0, 0, 
+		      backXPM.mask, ShapeSet);
+
+    if (!th_no_icon_window) {
+	XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
+			  backXPM.mask, ShapeSet);
+    } else {
+	if (strlen(th_icon_window) > 0) {
+	    XShapeCombineMask(Disp, Iconwin, ShapeBounding, 0, 0, 
+			      iconXPM.mask, ShapeSet);
+	}
+    }
+    if (debug) fprintf(stderr, "-> buttons ");
+    for (i=1; i <= but_max; i++) {
+	if ((thdata[i].panel == panel) || (thdata[i].panel == 0)) {
+	    if (strlen(thdata[i].xpm_file) > 0) {
+		if (debug) fprintf(stderr, "%d ", i);
+		if ((fak_use_alt(i)) && (strlen(thdata[i].altxpm_file) > 0)) {
+		    if (!th_no_icon_window) {
+			XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+				      thdata[i].altxpm.mask, ShapeUnion);
+			XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+					  thdata[i].altxpm.mask, ShapeUnion);
+		    } else {
+			if (thdata[i].icon) {
+			    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+					      thdata[i].altxpm.mask, ShapeUnion);
+			} else {
+			    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+					      thdata[i].altxpm.mask, ShapeUnion);
+			}
+		    }
+		} else {
+		    if (!th_no_icon_window) {
+			XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+					  thdata[i].xpm.mask, ShapeUnion);
+			XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+				      thdata[i].xpm.mask, ShapeUnion);
+		    } else {
+			if (thdata[i].icon) {
+			    XShapeCombineMask(Disp, Iconwin, ShapeBounding, thdata[i].x, thdata[i].y, 
+					      thdata[i].xpm.mask, ShapeUnion);
+			} else {
+			    XShapeCombineMask(Disp, Win, ShapeBounding, thdata[i].x, thdata[i].y, 
+					      thdata[i].xpm.mask, ShapeUnion);
+			}
+		    }
+		}
+	    }
+	}
+    }
+    if (debug) fprintf(stderr, "\n-> done.\n");
+}
+
+void fak_redraw()
+{
+    char txt[256];
+    int i;
+    long int dodo = 300000;
+    char cdtime[6];
+    int disp_time;
+    int use_alt = FALSE;
+    int offset;
+    DIR *dir_fd;
+    struct dirent *dir_pt;
+    unsigned int fx;
+    unsigned int fy;
+    unsigned int fw;
+    unsigned int fh;
+
+    /* ============================================================== */
+    /* ============================================================== */
+    /* ============================================================== */
+    /* First part: check different modes before doing the real redraw */
+    /* ============================================================== */
+    /* ============================================================== */
+    /* ============================================================== */
+
+    if (debug) fprintf(stderr, "** Entering redraw routine\n");
+					     
+    if (!blind_mode) {
+	if ((cur_cdmode == WM_CDM_PLAYING) || (wanna_play)) wm_cd_status();
+    }
+
+    /* the panel auto-switch mode: */
+
+    if (((cur_cdmode == WM_CDM_STOPPED) || (cur_cdmode == WM_CDM_EJECTED)) && (panel_stop > 0)) panel = panel_stop;
+    if (((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) && (panel_play > 0)) panel = panel_play;
+
+    /* ===================================================================
+       Auto-Repeat mode: at then end of the CD, play again the first track
+       =================================================================== */
+
+    if ((cur_track >= cur_ntracks) && (cur_cdmode != WM_CDM_PAUSED) && (cur_cdmode != WM_CDM_PLAYING)) {
+	/*if (wanna_play) {*/
+	if (autorepeat) {
+	    newtext("A.Repeat");
+	    do_autorepeat = TRUE;
+	    cd_control(PLAY);
+	} else {
+	    cd_control(STOPONLY); /* added in 0.13 */
+	    if (debug) fprintf(stderr, "-> Stopping\n");
+	    cur_cdmode = WM_CDM_STOPPED;
+	    wm_cd_status();
+	    cur_track = 0;
+	    redraw = TRUE;
+	    fak_maskset();
+	} 
+    }
+
+    /* ======================================================= */
+    /* the next track handling. We have to skip 'avoid' tracks */
+    /* ======================================================= */
+
+    if ((cur_cdmode != WM_CDM_EJECTED) && (! ignore_avoid)) {
+	if (cur_ntracks > 1) { /* only if there are more than ONE track */
+	    if (cur_track != old_track) {
+		if (debug) fprintf(stderr, "-> Track change (current = %d, old = %d)\n", cur_track, old_track);
+		redraw = TRUE;
+		i = cur_track - 1;
+		while ((i <= cur_ntracks) && (cd->trk[i].avoid == 1)) {
+		    i ++;
+		}
+
+		/* go to the next valid track, only if it is now the
+		   current track... */
+
+		if (i != cur_track - 1) {
+		    direct_track = i + 1;
+		    cd_control(DIRECTTRACK);
+		}
+	    }
+	}
+    }
+
+    /* ======================================================================
+       Do we *really* need to update the counter? If the cur time is the same 
+       as last time, we simply exit the redraw function
+       ====================================================================== */
+
+    if ((cur_track == old_track) && (lasttime == cur_pos_rel) && (!redraw) && (text_start == 0) && (strlen(led_text) == 0)) return;
+
+    lasttime = cur_pos_rel;
+
+    /* ===================================== */
+    /* ===================================== */
+    /* ===================================== */
+    /* ===================================== */
+    /* Dealing with the __real__ redraw now: */
+    /* ===================================== */
+    /* ===================================== */
+    /* ===================================== */
+    /* ===================================== */
+
+    if (debug) fprintf(stderr, "-> Redrawing\n");
+
+    fak_flush_expose(Win);
+    fak_flush_expose(Iconwin);
+
+
+    /* ================================ */
+    /* draw the text if we have a query */
+    /* ================================ */
+
+    /* but but but... If we see a "redraw" query, 
+       we skip the text! It will be displayed next time
+       the function is called... 0.13 */
+
+    if (!redraw) {
+	if (strlen(led_text) > 0) {
+	    if (text_start == 0) {
+		text_start = time(NULL);
+		fak_text("", MSG_PANEL, 0, FALSE);
+		fak_text(led_text, MSG_PANEL, 0, FALSE);
+	    }
+	    
+	    if (time(NULL) - text_start > text_timeout) {
+		text_start = 0;
+		redraw = TRUE;
+		strcpy(led_text, "");
+		fak_text("", MSG_PANEL, 0, FALSE);
+	    } else { 
+		if (!fade_out) return;
+	    }
+	}
+    }
+
+    /* ======================================= */
+    /* if WINGs support, update the track list */
+    /* ======================================= */
+
+#ifdef WMK
+    if (cur_track != old_track) {
+	update_track();
+	old_track = cur_track;
+    }
+#endif
+
+    /* ===============================================
+       We don't redraw everything if not required,
+       so whe ckech fo each element if redraw variable
+       is set
+       =============================================== */
+
+    /* the background: */
+
+    if (redraw) {
+
+	if (debug) fprintf(stderr, "-> ** GLOBAL REDRAW NEEDED **\n");
+
+	XCopyArea(Disp,backXPM.pixmap,Win,WinGC,0,0,backXPM.attributes.width, backXPM.attributes.height,0,0);
+	if (!th_no_icon_window) {
+	    XCopyArea(Disp,backXPM.pixmap,Iconwin,WinGC,0,0,backXPM.attributes.width, backXPM.attributes.height,0,0);
+	} else {
+	    if (strlen(th_icon_window) > 0) {
+		XCopyArea(Disp,iconXPM.pixmap,Iconwin,WinGC,0,0,iconXPM.attributes.width, iconXPM.attributes.height,0,0);
+	    }
+	}
+    }
+    
+    /* the buttons: */
+    
+    for (i = 1; i <= but_max; i++) {
+	
+	use_alt = FALSE;
+	
+	if ((thdata[i].panel == panel) || (thdata[i].panel == 0)) {
+	    if (thdata[i].type == FAK_VCD_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    if ((time_mode == 2) || (time_mode == 3)) {
+			if (cur_cdlen > 0) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      thdata[i].xpm.attributes.width, 
+				      0 + (int)((float)cur_pos_abs / (float)cur_cdlen * (float)thdata[i].xpm.attributes.height), 
+				      thdata[i].x, 
+				      thdata[i].y);
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  thdata[i].xpm.attributes.width, 
+					  0 + (int)((float)cur_pos_abs / (float)cur_cdlen * (float)thdata[i].xpm.attributes.height), 
+					  thdata[i].x, 
+					  thdata[i].y);
+			    }
+			}
+		    } else {
+			if (cur_tracklen > 0) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      thdata[i].xpm.attributes.width, 
+				      0 + (int)((float)cur_pos_rel / (float)cur_tracklen * (float)thdata[i].xpm.attributes.height), 
+				      thdata[i].x, 
+				      thdata[i].y);
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  thdata[i].xpm.attributes.width, 
+					  0 + (int)((float)cur_pos_rel / (float)cur_tracklen * (float)thdata[i].xpm.attributes.height), 
+					  thdata[i].x, 
+					  thdata[i].y);
+			    }
+			}
+		    }
+		}
+
+	    } else if (thdata[i].type == FAK_ICD_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    if ((time_mode == 2) || (time_mode == 3)) {
+			if (cur_cdlen > 0) {
+			    offset = (int)((float)cur_pos_abs / (float)cur_cdlen * (float)thdata[i].xpm.attributes.height);
+			    offset = thdata[i].xpm.attributes.height - offset;
+
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      thdata[i].xpm.attributes.width, 
+				      thdata[i].xpm.attributes.height - offset,
+				      thdata[i].x, 
+				      thdata[i].y +offset);
+			    
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  thdata[i].xpm.attributes.width, 
+					  thdata[i].xpm.attributes.height - offset,
+					  thdata[i].x, 
+					  thdata[i].y +offset);
+			    }
+			}
+		    } else {
+			if (cur_tracklen > 0) {
+			    offset = (int)((float)cur_pos_rel / (float)cur_tracklen * (float)thdata[i].xpm.attributes.height);
+			    offset = thdata[i].xpm.attributes.height - offset;
+
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      thdata[i].xpm.attributes.width, 
+				      thdata[i].xpm.attributes.height - offset,
+				      thdata[i].x, 
+				      thdata[i].y +offset);
+			    
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  thdata[i].xpm.attributes.width, 
+					  thdata[i].xpm.attributes.height - offset,
+					  thdata[i].x, 
+					  thdata[i].y +offset);
+			    }
+			}
+		    }
+		}
+
+	    } else if (thdata[i].type == FAK_CD_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    if ((time_mode == 2) || (time_mode == 3)) {
+			if (cur_cdlen > 0) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      0 + (int)((float)cur_pos_abs / (float)cur_cdlen * (float)thdata[i].xpm.attributes.width), 
+				      thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  0 + (int)((float)cur_pos_abs / (float)cur_cdlen * (float)thdata[i].xpm.attributes.width), 
+					  thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+			    }
+			}
+		    } else {
+			if (cur_tracklen > 0) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+				      0 + (int)((float)cur_pos_rel / (float)cur_tracklen * (float)thdata[i].xpm.attributes.width), 
+				      thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+					  0 + (int)((float)cur_pos_rel / (float)cur_tracklen * (float)thdata[i].xpm.attributes.width), 
+					  thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+			    }
+			}
+		    }
+		}
+
+	    } else if (thdata[i].type == FAK_VCD_PIX) { /************** vertical pixmap slider *****************/
+		if ((cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) && (cur_cdlen > 0)) {
+
+		    /* erase the old position : */
+
+		    if (thdata[i].ox > 0) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 
+				  0, thdata[i].oy, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].x, 
+				  thdata[i].oy + thdata[i].y);
+			
+			if (!th_no_icon_window) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 
+				      0, thdata[i].oy,
+				      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				      thdata[i].x, 
+				      thdata[i].oy + thdata[i].y);
+			}
+		    }
+
+		    /* compute the new geometry: */
+
+		    if ((time_mode == 2) || (time_mode == 3)) fx = (int)((float)cur_pos_abs / (float)cur_cdlen * (float)(thdata[i].xpm.attributes.width - thdata[i].altxpm.attributes.width));
+		    else fx = (int)((float)cur_pos_rel / (float)cur_tracklen * (float)(thdata[i].xpm.attributes.width - thdata[i].altxpm.attributes.width));
+
+		    /* ugly, need to CHANGE THIS!!!!! */
+		    if (fx < 1) fx = 1;
+
+		    /* and show it: */
+
+		    XCopyArea(Disp, thdata[i].altxpm.pixmap, Win, WinGC, 
+			      0, 0, 
+			      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+			      thdata[i].x, 
+			      thdata[i].y + fx);
+
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].altxpm.pixmap, Iconwin, WinGC, 
+				  0, 0, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].x, 
+				  thdata[i].y + fx);
+		    }
+
+		    /* finaly, save it! */
+		    thdata[i].oy = fx;
+		}
+	    } else if (thdata[i].type == FAK_CD_PIX) { /**************** horizontal pixmap slider *****************/
+		if ((cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) && (cur_cdlen > 0)) {
+		    /* erase the old position : */
+
+		    if (thdata[i].ox > 0) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 
+				  thdata[i].ox, 0, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].ox + thdata[i].x, 
+				  thdata[i].y);
+			
+			if (!th_no_icon_window) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 
+				      thdata[i].ox, 0, 
+				      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				      thdata[i].ox + thdata[i].x, 
+				      thdata[i].y);
+			}
+		    }
+
+		    /* compute the new geometry: */
+
+		    if ((time_mode == 2) || (time_mode == 3)) fx = (int)((float)cur_pos_abs / (float)cur_cdlen * (float)(thdata[i].xpm.attributes.height - thdata[i].altxpm.attributes.height));
+		    else fx = (int)((float)cur_pos_rel / (float)cur_tracklen * (float)(thdata[i].xpm.attributes.height - thdata[i].altxpm.attributes.height));
+
+		    /* ugly, need to CHANGE THIS!!!!! */
+		    if (fx < 1) fx = 1;
+
+		    /* and show it: */
+
+		    XCopyArea(Disp, thdata[i].altxpm.pixmap, Win, WinGC, 
+			      0, 0, 
+			      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+			      thdata[i].x + fx, 
+			      thdata[i].y);
+
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].altxpm.pixmap, Iconwin, WinGC, 
+				  0, 0, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].x + fx, 
+				  thdata[i].y);
+		    }
+
+		    /* finaly, save it! */
+		    thdata[i].ox = fx;
+		}
+	    } else if (thdata[i].type == FAK_VOL_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+			      0 + (int)((float)volume / (float)max_volume * (float)thdata[i].xpm.attributes.width), 
+			      thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+				  0 + (int)((float)volume / (float)max_volume * (float)thdata[i].xpm.attributes.width), 
+				  thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+		    }
+		}
+	    } else if (thdata[i].type == FAK_VOL_PIX) {
+		if ((cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED)) {
+
+		    /* first erase the old position : */
+
+		    if (thdata[i].ox > 0) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 
+				  thdata[i].ox, 0, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].ox + thdata[i].x, 
+				  thdata[i].y);
+			
+			if (!th_no_icon_window) {
+			    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 
+				      thdata[i].ox, 0, 
+				      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				      thdata[i].ox + thdata[i].x, 
+				      thdata[i].y);
+			}
+		    }
+
+		    /* compute the new geometry: */
+
+		    fx = (int)((float)volume / (float)max_volume * (float)(thdata[i].xpm.attributes.width - thdata[i].altxpm.attributes.width));
+
+		    /* ugly, need to CHANGE THIS!!!!! */
+		    if (fx < 1) fx = 1;
+
+		    /* and show it: */
+
+		    XCopyArea(Disp, thdata[i].altxpm.pixmap, Win, WinGC, 
+			      0, 0, 
+			      thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+			      thdata[i].x + fx, 
+			      thdata[i].y);
+
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].altxpm.pixmap, Iconwin, WinGC, 
+				  0, 0, 
+				  thdata[i].altxpm.attributes.width, thdata[i].altxpm.attributes.height,
+				  thdata[i].x + fx, 
+				  thdata[i].y);
+		    }
+
+		    /* finaly, save it! */
+		    thdata[i].ox = fx;
+		}
+	    } else if (thdata[i].type == FAK_VVOL_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+			      thdata[i].xpm.attributes.width, 
+			      0 + (int)((float)volume / (float)max_volume * (float)thdata[i].xpm.attributes.height), 
+			      thdata[i].x, 
+			      thdata[i].y);
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+			      thdata[i].xpm.attributes.width, 
+			      0 + (int)((float)volume / (float)max_volume * (float)thdata[i].xpm.attributes.height), 
+			      thdata[i].x, 
+			      thdata[i].y);
+		    }
+		}
+	    } else if (thdata[i].type == FAK_IVOL_BAR) {
+		if ( (cur_cdmode != WM_CDM_STOPPED) && (cur_cdmode != WM_CDM_EJECTED) ) {
+		    offset = (int)((float)volume / (float)max_volume * (float)thdata[i].xpm.attributes.height);
+		    offset = thdata[i].xpm.attributes.height - offset;
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, offset, 
+			      thdata[i].xpm.attributes.width, 
+			      thdata[i].xpm.attributes.height - offset,
+			      thdata[i].x, 
+			      thdata[i].y +offset);
+		    if (!th_no_icon_window) {
+			XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, offset, 
+			      thdata[i].xpm.attributes.width, 
+			      thdata[i].xpm.attributes.height - offset,
+			      thdata[i].x, 
+			      thdata[i].y +offset);
+		    }
+		}
+	    } else if (thdata[i].type == FAK_MIXER_BAR) {
+#ifdef MIXER
+		XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+			  0 + (int)((float)getvol(thdata[i].arg) / 100.0 * (float)thdata[i].xpm.attributes.width), 
+			  thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+		if (!th_no_icon_window) {
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+			      0 + (int)((float)getvol(thdata[i].arg) / 100.0 * (float)thdata[i].xpm.attributes.width), 
+			      thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+		}
+#endif
+	    } else if (thdata[i].type == FAK_VMIXER_BAR) {
+#ifdef MIXER
+		XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, 
+			  thdata[i].xpm.attributes.width, 
+			  0 + (int)((float)getvol(thdata[i].arg) / 100.0 * (float)thdata[i].xpm.attributes.height), 
+			  thdata[i].x, 
+			  thdata[i].y);
+		if (!th_no_icon_window) {
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, 
+			      thdata[i].xpm.attributes.width, 
+			      0 + (int)((float)getvol(thdata[i].arg) / 100.0 * (float)thdata[i].xpm.attributes.height), 
+			      thdata[i].x, 
+			      thdata[i].y);
+		}
+#endif
+	    } else if (thdata[i].type == FAK_IMIXER_BAR) {
+#ifdef MIXER
+		offset = (int)((float)getvol(thdata[i].arg) / 100.0 * (float)thdata[i].xpm.attributes.height);
+		offset = thdata[i].xpm.attributes.height - offset;
+		XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 
+			  0, 
+			  offset, 
+			  thdata[i].xpm.attributes.width, 
+			  thdata[i].xpm.attributes.height - offset,
+			  thdata[i].x, 
+			  thdata[i].y + offset);
+		if (!th_no_icon_window) {
+		    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, offset, 
+			      thdata[i].xpm.attributes.width, 
+			      thdata[i].xpm.attributes.height - offset,
+			      thdata[i].x, 
+			      thdata[i].y + offset);
+		}
+#endif
+	    } else {
+		if (redraw) {
+		    if (strlen(thdata[i].xpm_file) > 0) {
+			if (strlen(thdata[i].altxpm_file) == 0) use_alt = FALSE;
+			else use_alt = fak_use_alt(i);
+			
+			if (use_alt) {
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].altxpm.pixmap, Win, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].altxpm.attributes.height, thdata[i].x, thdata[i].y);
+				XCopyArea(Disp, thdata[i].altxpm.pixmap, Iconwin, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].altxpm.attributes.height, thdata[i].x, thdata[i].y);
+			    } else {
+				if (thdata[i].icon) {
+				    XCopyArea(Disp, thdata[i].altxpm.pixmap, Iconwin, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].altxpm.attributes.height, thdata[i].x, thdata[i].y);
+				} else {
+				    XCopyArea(Disp, thdata[i].altxpm.pixmap, Win, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].altxpm.attributes.height, thdata[i].x, thdata[i].y);
+				}
+			    }
+			} else {
+			    if (!th_no_icon_window) {
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+				XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+			    } else {
+				if (thdata[i].icon) {
+				    XCopyArea(Disp, thdata[i].xpm.pixmap, Iconwin, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+				} else {
+				    XCopyArea(Disp, thdata[i].xpm.pixmap, Win, WinGC, 0, 0, thdata[i].xpm.attributes.width, thdata[i].xpm.attributes.height, thdata[i].x, thdata[i].y);
+				}
+			    }
+			}
+		    }
+		}
+	    }
+	}
+    }
+    
+    redraw = FALSE;
+   
+   if (debug) fprintf(stderr, "-> Display [last section]\n");
+
+    /* The Track number */
+
+    /* if ((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) { */
+    if (!th_no_minus) fak_text("      ", COUNTER_PANEL, 0, FALSE);
+    else fak_text("     ", COUNTER_PANEL, 0, FALSE);
+
+    /* no CD? no counters! */
+
+    if (cur_cdmode == WM_CDM_EJECTED) {
+	if (!th_no_minus) fak_text("AScd", COUNTER_PANEL, 1, FALSE);
+	else fak_text("AScd", COUNTER_PANEL, 0, FALSE);
+	fak_text("NO CD", MSG_PANEL, 1, TRUE);
+        if (debug) fprintf(stderr, "-> No CD. Leaving redraw routine\n");
+	return;
+    }
+
+    if (debug) fprintf(stderr, "-> Dealing with theme/fast_track selectors\n");
+
+    /* ================== */
+    /* The theme selector */
+    /* ================== */
+
+    if (theme_select > 0) {
+	/* 5 seconds timeout */
+	if (time(NULL) - selectors_timeout <= 5) {
+	    fak_text("THEME", COUNTER_PANEL, 0, TRUE);
+	    sprintf(cdtime, "%02d", theme_select);
+	    fak_text(cdtime, TRACK_PANEL, 0, FALSE);
+	    
+	    i = 0;
+
+	    sprintf(txt, "%s/Themes", THDIR);
+	    if ((dir_fd = opendir(txt)) != NULL) {
+		i = 0;
+		while((dir_pt = readdir(dir_fd)) != NULL) {
+		    if (dir_pt->d_name[0] != '.') {
+			i++;
+			if (i == theme_select) {
+			    strcpy(selected_theme, dir_pt->d_name);
+			    fak_text(dir_pt->d_name, MSG_PANEL, 0, FALSE);
+			}
+		    }
+		}
+		closedir(dir_fd);
+	    }
+	    return;
+	} else {
+	    theme_select = 0;
+	    redraw = TRUE;
+	}
+    }
+
+    /* =================== */
+    /* Fast Track selector */
+    /* =================== */
+
+    if (fast_track == 0) {
+	if (cur_track > 0) {
+	    /* 15 seconds timeout */
+	    if (time_mode != 15) {
+		sprintf(cdtime, "%02d", cur_track);
+	    } else {
+		sprintf(cdtime, "%02d", cur_ntracks - cur_track);
+	    }
+	} else {
+	    sprintf(cdtime, "%02d", cur_ntracks);
+	}
+        if ((thdata[but_tracknbr].panel == panel) || (thdata[but_tracknbr].panel == 0))
+	    fak_text(cdtime, TRACK_PANEL, 0, TRUE);
+    } else {
+	if (time(NULL) - selectors_timeout <= 3) {
+	    sprintf(cdtime, "%02d", fast_track);
+	    fak_text("TRACK", COUNTER_PANEL, 0, TRUE);
+	    if ((thdata[but_tracknbr].panel == panel) || (thdata[but_tracknbr].panel == 0))
+		fak_text(cdtime, TRACK_PANEL, 0, FALSE);
+	    return;
+	} else {
+	    selectors_timeout = 0;
+	    fast_track = 0;
+	    redraw = TRUE;
+	}
+    }
+
+    if (debug) fprintf(stderr, "-> Counter\n");
+
+   /* the counter: */
+
+    switch(time_mode) {
+    case 0:
+	fak_minus();
+	disp_time = cur_pos_rel;
+	break;
+    case 1:
+	fak_minus();
+	disp_time = cur_tracklen - cur_pos_rel ;
+	break;
+    case 2:
+	fak_minus();
+	disp_time = cur_pos_abs;
+	break;
+    default:
+	fak_minus();
+	disp_time = cur_cdlen - cur_pos_abs;
+	break;
+    }
+
+    strcpy(cdtime, "00:00");
+    if ((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) {
+	cdtime[0] = 0 ;
+	cdtime[1] = (disp_time / 60);
+	if (cdtime[1] >= 10) {
+	    cdtime[0] = (cdtime[1] / 10);
+	    cdtime[1] = (cdtime[1] % 10);
+	}
+	cdtime[3] = ((disp_time % 60) / 10);
+	cdtime[4] = (disp_time % 10);
+    } else {
+	cdtime[0] = 0;
+	cdtime[1] = (cur_cdlen / 60);
+	if (cdtime[1] >= 10) {
+	    cdtime[0] = (cdtime[1] / 10);
+	    cdtime[1] = (cdtime[1] % 10);
+	}
+	cdtime[3] = ((cur_cdlen % 60) / 10);
+	cdtime[4] = (cur_cdlen % 10);
+    }
+
+    cdtime[0] = cdtime[0] + 48;
+    cdtime[1] = cdtime[1] + 48;
+    cdtime[3] = cdtime[3] + 48;
+    cdtime[4] = cdtime[4] + 48;
+
+    if (th_no_minus) fak_text(cdtime, 0, 0, FALSE);
+    else fak_text(cdtime, 0, 1, FALSE);
+
+    /* ================= */
+    /* The auto fade out */
+    /* ================= */
+
+    if ((cur_cdmode == WM_CDM_PLAYING) && (fade_out)) {
+	if (!fade_ok) {
+	    fade_ok = 1;
+	    return;
+	} else {
+	    unmuted_volume = volume ;
+	    while (volume > min_volume) {
+		volume = volume - fade_step;
+		cd_volume(volume, 10, max_volume);
+		usleep(dodo);
+	    }
+	    fade_out = 0;
+	    fade_ok = 0;
+	    cd_control(PAUSE);
+	    old_track = 0;
+	    volume = unmuted_volume;
+	    cd_volume(volume, 10, max_volume);
+	}
+    }
+
+    /* ================= */
+    /* The auto fade in: */
+    /* ================= */
+
+    if ((cur_cdmode == WM_CDM_PAUSED) && (fade_out)){
+	if (!fade_ok) {
+	    fade_ok = 1;
+	    return;
+	} else {
+	    unmuted_volume = volume;
+	    volume = min_volume;
+	    cd_volume(volume, 10, max_volume);
+	    wm_cd_status();
+	    usleep(dodo * 5);
+	    cd_control(PAUSE);
+	    usleep(dodo * 5);
+	    while (volume < max_volume) {
+		volume = volume + fade_step;
+		cd_volume(volume, 10, max_volume);
+		usleep(dodo);
+	    }
+	    fade_out = 0;
+	    fade_ok = 0;
+	    volume = unmuted_volume;
+	    cd_volume(volume, 10, max_volume);
+	}
+    }
+
+    /* end of redraw routine */
+
+    old_track = cur_track;
+    if (debug) fprintf(stderr, "-> End of global redraw. Leaving redraw routine\n");
+}
diff --git a/ascd/ascd/faktory_prot.h b/ascd/ascd/faktory_prot.h
new file mode 100644
index 0000000..5dc4912
--- /dev/null
+++ b/ascd/ascd/faktory_prot.h
@@ -0,0 +1,15 @@
+extern void tes_sncpy(char *, char *, int);
+extern char *tes_xgets(char *, int , FILE *);
+extern int fak_parse_line(char *, char *, char *);
+extern int fak_use_alt(int);
+extern void fak_validate_pixmap(char *, char *);
+extern void fak_init_theme(int); 
+extern int fak_load_theme(char *, int); 
+extern void fak_icon_text(char *, unsigned int, unsigned int, unsigned int); 
+extern void fak_text(char *, unsigned int, unsigned int, unsigned int);
+extern int fak_flush_expose(Window);
+extern void fak_minus(void);
+extern void fak_singlemask(int);
+extern void fak_maskset();
+extern void fak_redraw();
+
diff --git a/ascd/ascd/handlers.c b/ascd/ascd/handlers.c
new file mode 100644
index 0000000..303817f
--- /dev/null
+++ b/ascd/ascd/handlers.c
@@ -0,0 +1,445 @@
+/* ===========================================================================
+ * AScd: handlers.c
+ * mouse events <-> integrated commands
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+ 								   
+#include "ext.h"
+#include "faktory_prot.h"
+
+void theme_select_init()
+{
+    char txt[256];
+    DIR *dir_fd;
+    struct dirent *dir_pt;
+    int i;
+
+    theme_select = 1;
+    theme_select_nbr = 0;
+
+    sprintf(txt, "%s/Themes", THDIR);
+    if ((dir_fd = opendir(txt)) != NULL) {
+	while((dir_pt = readdir(dir_fd)) != NULL) if (dir_pt->d_name[0] != '.') theme_select_nbr++;
+	closedir(dir_fd);
+    }
+    
+    sprintf(txt, "%s/Themes", THDIR);
+    if ((dir_fd = opendir(txt)) != NULL) {
+	i = 0;
+	while((dir_pt = readdir(dir_fd)) != NULL) {
+	    if (dir_pt->d_name[0] != '.') {
+		i++;
+		if (strcmp(dir_pt->d_name, theme) == 0) theme_select = i;
+	    }
+	}
+	closedir(dir_fd);
+    }
+}
+
+void fak_event_handle(int event, XEvent Event)
+{
+
+    /* global commands handling: panel switches, quit, GUI additional modules */
+
+    switch (event) {
+    case FAK_QUIT:
+	exit(0);
+	break;
+    case FAK_PANEL_SWITCH:
+	panel++;
+	if (panel > panels) panel = 1;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_PANEL1:
+	panel = 1;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_PANEL2:
+	panel = 2;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_PANEL3:
+	panel = 3;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_PANEL4:
+	panel = 4;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_PANEL5:
+	panel = 5;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_WINGS: /* must be compiled with WINGs support */
+#ifdef WMK
+	big_window(scr);
+#endif
+	break;
+    case FAK_COUNTER_MODE:
+	time_mode++ ;
+	if (time_mode > 3) time_mode = 0;
+	break;
+    case FAK_TOG_SHOWDB:
+	if (show_db) show_db = FALSE;
+	else show_db = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TOG_SHOWARTIST:
+	if (show_artist) show_artist = FALSE;
+	else show_artist = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TOG_UPPER:
+	if (force_upper) force_upper = FALSE;
+	else force_upper = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TOG_AUTOPLAY:
+	if (autoplay) autoplay = FALSE;
+	else autoplay = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TOG_AUTOREPEAT:
+	if (autorepeat) autorepeat = FALSE;
+	else autorepeat = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TOG_ISKIPS:
+	if (ignore_avoid) ignore_avoid = FALSE;
+	else ignore_avoid = TRUE;
+	redraw = TRUE;
+	break;
+    case FAK_TSELECT:
+	if (theme_select == 0) {
+	    selectors_timeout = time(NULL);
+	    theme_select_init();
+	} else {
+	    theme_select = 0;
+	    strcpy(theme, selected_theme);
+	    fak_load_theme(theme, TRUE);
+	    fak_maskset();
+	}
+	break;
+    case FAK_TNEXT:
+	selectors_timeout = time(NULL);
+	if (theme_select == 0) theme_select_init();
+	theme_select ++;
+	if (theme_select > theme_select_nbr) theme_select = 1;
+	break;
+    case FAK_TPREVIOUS:
+	selectors_timeout = time(NULL);
+	if (theme_select == 0) theme_select_init();
+	theme_select --;
+	if (theme_select == 0) theme_select = theme_select_nbr;
+	break;
+    case FAK_FTSELECT:
+	show_db_pos = 0;
+	if (fast_track == 0) {
+	    fast_track = cur_track;
+	    selectors_timeout = time(NULL);
+	} else {
+	    if (fast_track != cur_track) {
+		direct_track = fast_track;
+		fast_track = 0;
+		cd_control(DIRECTTRACK);
+	    }
+	}
+	break;
+    case FAK_FTNEXT:
+	selectors_timeout = time(NULL);
+	show_db_pos = 0;
+	if (fast_track == 0) {
+	    if (cur_track < cur_ntracks) fast_track = cur_track + 1;
+	    else fast_track = 1;
+	} else {
+	    if (fast_track == cur_ntracks) fast_track = 1;
+	    else fast_track++;
+	}
+	break;
+    case FAK_FTPREVIOUS:
+	selectors_timeout = time(NULL);
+	show_db_pos = 0;
+	if (fast_track == 0) {
+	    if (cur_track > 1) fast_track = cur_track - 1;
+	    else fast_track = cur_ntracks;
+	} else {
+	    if (fast_track == 1) fast_track = cur_ntracks;
+	    else fast_track--;
+	}
+	break;
+    case FAK_SAVE:
+	newtext("Saving");
+	save_rc_file();
+	break;
+    case FAK_LOAD:
+	newtext("Loading");
+	load_rc_file();
+	break;
+    case FAK_QREF:
+	quick_reference(thdata[but_current].arg);
+	break;
+    default:
+	break;
+    }
+
+    redraw = TRUE;
+    fak_singlemask(but_current);
+}
+
+void cd_event_handle(int event, XEvent Event)
+{
+    /* All the AScd CD player commands, access to cd_control() */
+
+    switch (event) {
+    case FAK_CD_PLAY:
+    case FAK_CD_PAUSE:
+	if (cur_cdmode == WM_CDM_PAUSED) {
+	    newtext("Playing");
+	    wanna_play = TRUE;
+	    cd_control(PAUSE);
+	} else if (cur_cdmode == WM_CDM_PLAYING) {
+	    if (fast_track != 0) {
+		direct_track = fast_track;
+		fast_track = 0;
+		cd_control(DIRECTTRACK);
+	    } else {
+		newtext("Paused");
+		cd_control(PAUSE);
+	    }
+	} else if (cur_cdmode == WM_CDM_EJECTED) {
+	    /* the future?. Not yet working on my FreeBSD box... */
+	    cd_control(CLOSETRAY);
+	} else {
+	    newtext("Play");
+	    wanna_play = TRUE;
+	    cd_control(PLAY);
+	    if (cd->volume > 0) {
+		volume=cd->volume;
+		cd_volume(volume, 10, max_volume);
+	    }
+	}
+	redraw = TRUE;
+	fak_redraw();
+	break;
+    case FAK_CD_STOP:
+	fast_track = 0;
+	if (cur_cdmode != WM_CDM_STOPPED) {
+	    newtext("Stop");
+	    cd_control(STOPONLY);
+	    wm_cd_status();
+	    cur_track = 0;
+	    redraw = TRUE;
+	    fak_redraw();
+	}
+	break;
+    case FAK_CD_STOPEJECT:
+	fast_track = 0;
+	if (cur_cdmode != WM_CDM_STOPPED) {
+	    newtext("Stop");
+	    cd_control(STOPONLY);
+	    wm_cd_status();
+	    cur_track = 0;
+	    redraw = TRUE;
+	    fak_redraw();
+	    fak_maskset();
+	} else {
+	    newtext("Eject");
+	    cd_control(EJECT);
+	    redraw = TRUE;
+	    fak_redraw();
+	}
+	break;
+    case FAK_CD_EJECT:
+	fast_track = 0;
+	newtext("Eject");
+	cd_control(EJECT);
+	redraw = TRUE;
+	fak_redraw();	
+	break;
+    case FAK_CD_EJECTQUIT:
+	fast_track = 0;
+	if ((cur_cdmode != WM_CDM_EJECTED) && (cur_cdmode != WM_CDM_STOPPED)) {
+	    newtext("Eject");
+	    cd_control(EJECT);
+	    redraw = TRUE;
+	    fak_redraw();
+	} else {
+	    exit(0);
+	}
+	break;
+    case FAK_CD_NEXT:
+	cd_control(UPTRACK);
+	break;
+    case FAK_CD_PREVIOUS:
+	cd_control(DNTRACK);
+	break;
+    case FAK_CD_FIRST:
+	cd_control(FIRST);
+	break;
+    case FAK_CD_LAST:
+	cd_control(LAST);
+	break;
+    case FAK_CD_REW:
+	cd_control(REV);
+	break;
+    case FAK_CD_FWD:
+	cd_control(CUE);
+	break;
+    case FAK_CD_DIRECT:
+	/* Direct Access:
+	   we have to compute the offset to pass it to cd_control. We also have to
+	   check if we want to move inside the current track or in the whole CD */
+	
+	if (cur_track < 1) cur_track = 1;
+	
+	if ((time_mode == 2) || (time_mode == 3)) {
+	    if ((thdata[but_current].type == FAK_CD_BAR) || (thdata[but_current].type == FAK_CD_PIX)) {
+		direct_access = (int)((float)(Event.xbutton.x - thdata[but_current].x) / (float)thdata[but_current].xpm.attributes.width * (float)cur_cdlen);
+	    } else if (thdata[but_current].type == FAK_ICD_BAR) {
+		direct_access = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)cur_cdlen);
+		direct_access = cur_cdlen - direct_access;
+	    } else {
+		direct_access = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)cur_cdlen);
+	    }
+	    cd_control(GLOBALACCESS);
+	} else {
+	    if ((thdata[but_current].type == FAK_CD_BAR) || (thdata[but_current].type == FAK_CD_PIX)) {
+		direct_access = (int)((float)(Event.xbutton.x - thdata[but_current].x) / (float)thdata[but_current].xpm.attributes.width * (float)cur_tracklen);
+	    } else if (thdata[but_current].type == FAK_ICD_BAR) {
+		direct_access = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)cur_tracklen);
+		direct_access = cur_tracklen - direct_access;
+	    } else {
+		direct_access = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)cur_tracklen);
+	    }
+	    cd_control(DIRECTACCESS);
+	}
+	wm_cd_status();
+	redraw = TRUE;
+	fak_redraw();
+	fak_maskset();
+	break;
+    case FAK_CD_VOLUME:
+	if ((cur_cdmode != WM_CDM_EJECTED) && (cur_cdmode != WM_CDM_STOPPED)) {
+	    if ((thdata[but_current].xpm.attributes.width > 0) && (max_volume > 0)) {
+		if (thdata[but_current].type == FAK_VVOL_BAR) {
+		    volume = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)max_volume);
+		} else if (thdata[but_current].type == FAK_IVOL_BAR) {
+		    volume = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * (float)max_volume);
+		    volume = max_volume - volume;
+		} else {
+		    volume = (int)((float)(Event.xbutton.x - thdata[but_current].x) / (float)thdata[but_current].xpm.attributes.width * (float)max_volume);
+		}
+		cd_volume(volume, 10, max_volume);
+		wm_cd_status();
+		redraw = TRUE;
+		fak_redraw();
+	    }
+	}
+	break;
+    case FAK_CD_LOOP:
+	if (loop_mode) {
+	    loop_mode = 0;
+	    newtext("Loop Off");
+	} else {
+	    cd_control(LOOP);
+	    if (!anomalie) newtext("Loop On");
+	    else newtext("ERROR!");
+	}
+	redraw = TRUE;
+	break;
+    case FAK_CD_LSTART:
+	loop_1 = cur_pos_rel;
+	newtext("L. Start");
+	loop_start_track = cur_track;
+	break;
+    case FAK_CD_LEND:
+	loop_2 = cur_pos_rel;
+	loop_end_track = cur_track;
+	newtext("L. End");
+	break;
+    case FAK_CD_GOLSTART:
+	direct_access = loop_1;
+	cd_control(DIRECTACCESS);
+	break;
+    case FAK_CD_GOLEND:
+	direct_access = loop_2;
+	cd_control(DIRECTACCESS);
+	break;
+    case FAK_CD_LTRACK:
+	loop_1 = 0;
+	loop_2 = cur_tracklen;
+	loop_start_track = cur_track;
+	loop_end_track = cur_track;
+	break;
+    case FAK_CD_LCLEAR:
+	loop_1 = 0;
+	loop_2 = 0;
+	loop_start_track = 1;
+	loop_end_track = 1;
+	newtext("L. Clear");
+	break;
+    case FAK_CD_INTRO:
+	cd_control(INTROSCAN);
+	wm_cd_status();
+	newtext("Intro");
+	fak_maskset();
+	break;
+    case FAK_CD_MUTE:
+	if (muted) {
+	    muted = 0;
+	    newtext("Mute Off");
+	    cd_volume(volume, 10, max_volume);
+	} else {
+	    newtext("Mute On");
+	    muted = 1;
+	    cd_volume(muted_volume, 10, max_volume);
+	}
+	break;
+    case FAK_CD_FADE:
+	fade_out = TRUE;
+	newtext("Fade...");
+	/*fak_redraw();*/
+	break;
+    default:
+	break;
+    }
+
+    redraw = TRUE;
+    fak_singlemask(but_current);
+}
diff --git a/ascd/ascd/misc.c b/ascd/ascd/misc.c
new file mode 100644
index 0000000..80fca37
--- /dev/null
+++ b/ascd/ascd/misc.c
@@ -0,0 +1,431 @@
+/* ===========================================================================
+ * AScd: the AfterStep and WindowMaker CD player
+ * misc.c: various stuff
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+
+#include "ext.h"
+
+extern tes_xgets();
+extern fak_parse_line();
+extern cd_control_version();
+
+/* The rc file functions, taken from xfascd*/
+
+void load_rc_file(int mx)
+{
+    FILE *in;
+    struct passwd *pw;
+    char st[256];
+
+    if (NULL == (pw = getpwuid(getuid()))) {
+        return;
+    } else {
+        sprintf(st, "%s/.ascdrc", pw->pw_dir);
+        if ((in = fopen(st, "r"))) {
+            while ( fgets(st, 255, in) ) {
+                if (strlen(st) > 0) st[strlen(st) - 1] = 0;
+
+		if (!mx) {
+#ifndef NO_D_DEVICE
+		    if (strncmp(st, "CD_DEVICE=", 10) == 0) strcpy(cd_device, st + 10);
+		    else if (strncmp(st, "THEME=", 6) == 0) strcpy(theme, st + 6);
+		    else if (strncmp(st, "VISU=", 5) == 0) strcpy(xv, st + 5);
+		    else if (strncmp(st, "AUTOREPEAT=", 11) == 0) autorepeat = atoi(st + 11);
+#else
+		    if (strncmp(st, "AUTOREPEAT=", 11) == 0) autorepeat = atoi(st + 11);
+#endif
+		    else if (strncmp(st, "AUTOPLAY=", 9) == 0) autoplay = atoi(st + 9);
+		    else if (strncmp(st, "AUTOPROBE=", 10) == 0) autoprobe = atoi(st + 10);
+		    else if (strncmp(st, "CUE_TIME=", 9) == 0) cue_time = atoi(st + 9);
+		    else if (strncmp(st, "TI=", 3) == 0) text_timeout = atoi(st + 3);
+		    else if (strncmp(st, "MAX_VOL=", 8) == 0) max_volume = atoi(st + 8);
+		    else if (strncmp(st, "MIN_VOL=", 8) == 0) min_volume = atoi(st + 8);
+		    else if (strncmp(st, "MUT_VOL=", 8) == 0) muted_volume = atoi(st + 8);
+		    else if (strncmp(st, "VOLUME=", 7) == 0) volume = atoi(st + 7);
+		    else if (strncmp(st, "FADE_STEP=", 10) == 0) fade_step = atoi(st + 10);
+		    else if (strncmp(st, "TIMEMODE=", 9) == 0) time_mode = atoi(st + 9);
+		    else if (strcmp(st, "WITHDRAWN") == 0) withdrawn = TRUE;
+		    else if (strcmp(st, "SHOWDB") == 0) show_db = TRUE;
+		    else if (strcmp(st, "SHOWARTIST") == 0) show_artist = TRUE;
+		    else if (strcmp(st, "UPPERCASE") == 0) force_upper = TRUE;
+		    else if (strncmp(st, "IGNORE_AVOID=", 13) == 0) ignore_avoid = atoi(st + 13);
+		}
+            }
+            fclose(in);
+            return;
+        } else {
+            return;
+        }
+    }
+}
+
+void save_rc_file()
+{
+    FILE *out;
+    struct passwd *pw;
+    char st[128];
+
+    if (NULL == (pw = getpwuid(getuid()))) {
+	/* hum... big problem... */
+        return;
+    } else {
+        sprintf(st, "%s/.ascdrc", pw->pw_dir);
+        if ((out = fopen(st, "w"))) {
+            fprintf(out, "#### generated by AScd version %s ####\n", VERSION);
+#ifndef NO_D_DEVICE
+	    fprintf(out, "CD_DEVICE=%s\n", cd_device);
+#endif	   
+	    fprintf(out, "THEME=%s\n", theme);
+	    fprintf(out, "VISU=%s\n", xv);
+            fprintf(out, "AUTOREPEAT=%d\n", autorepeat);
+            fprintf(out, "AUTOPLAY=%d\n", autoplay);
+            fprintf(out, "AUTOPROBE=%d\n", autoprobe);
+            fprintf(out, "CUE_TIME=%d\n", cue_time);
+            fprintf(out, "TI=%d\n", text_timeout);
+            fprintf(out, "VOLUME=%d\n", volume);
+            fprintf(out, "MAX_VOL=%d\n", max_volume);
+            fprintf(out, "MIN_VOL=%d\n", min_volume);
+            fprintf(out, "MUT_VOL=%d\n", muted_volume);
+            fprintf(out, "FADE_STEP=%d\n", fade_step);
+	    fprintf(out, "TIMEMODE=%d\n", time_mode);
+            fprintf(out, "IGNORE_AVOID=%d\n", ignore_avoid);
+	    if (withdrawn) {
+		fprintf(out, "WITHDRAWN\n");
+	    }
+	    if (show_db) {
+		fprintf(out, "SHOWDB\n");
+	    }
+	    if (show_artist) {
+		fprintf(out, "SHOWARTIST\n");
+	    }
+	    if (force_upper) {
+		fprintf(out, "UPPERCASE\n");
+	    }
+
+            fclose(out);
+            return;
+        } else {
+            return;
+        }
+    }
+}
+
+void command_line_help() 
+{
+    fprintf(stderr, "ascd %s ", VERSION);
+#ifdef WMK
+    fprintf(stderr, "[WINGs] ");
+#endif
+#ifdef MIXER
+    fprintf(stderr, "[mixer] ");
+#endif
+    fprintf(stderr, "\n(c) 1999 Rob Malda and Denis Bourez\n");
+    fprintf(stderr, "Usage:  ascd [-options ...] where the options are:\n\n");
+
+    fprintf(stderr, "-version                 Display the version numbers and exit\n");
+    fprintf(stderr, "-help                    Display this help screen and exit\n");
+    fprintf(stderr, "-themes                  Display available themes and exit\n\n");
+
+    fprintf(stderr, "+/-withdrawn             Turn on/off withdrawn mode for WindowMaker\n");
+    fprintf(stderr, "-geometry geom           Standard X Location\n");
+    fprintf(stderr, "-ti time                 Texts display timeout in seconds (default = 1)\n");
+    fprintf(stderr, "-cue time                Cue Time in seconds (default = 10)\n");
+    fprintf(stderr, "+/-a                     Turn on/off Autoplay mode\n");
+    fprintf(stderr, "+/-p                     Turn on/off Autoprobe mode\n");
+    fprintf(stderr, "+/-t                     Turn on/off Autorepeat mode\n");
+    fprintf(stderr, "+/-B                     Turn on/off Blind mode (no counters updates)\n");
+    fprintf(stderr, "+/-ia                    Apply/Ignore 'avoid' tags in WorkMan database\n"),
+    fprintf(stderr, "+/-up                    All messages in uppercase?\n");
+    fprintf(stderr, "-theme value             Select the visual theme (in %s)\n", THDIR);
+    fprintf(stderr, "-xf                      Launch XfAscd with middle click on counter\n");
+    fprintf(stderr, "-minvolume value         Minimum volume\n");
+    fprintf(stderr, "-maxvolume value         Maximum volume\n");
+    fprintf(stderr, "-volume value            CD Drive volume, from 0 to maxvolume\n");
+    fprintf(stderr, "-mvolume value           CD Drive volume in mute mode, from 0 to maxvolume\n");
+    fprintf(stderr, "-fadestep value          Fade in/out timing offset\n");
+    fprintf(stderr, "-device device_driver    CDROM drive device\n");
+#ifdef MIXER
+    fprintf(stderr, "-mixer device_driver     Mixer device\n");
+#endif
+    fprintf(stderr, "-showtitle               Turn on Song Title Scrolling\n");
+    fprintf(stderr, "-showartist              Show artist name when scrolling song title\n");
+    fprintf(stderr, "-debug                   Enable debug (verbose) messages on stderr\n");
+    fprintf(stderr, "-save                    Save settings and exit. Should be *last* command.\n\n");
+    exit(0);
+} 
+
+void command_line_err() 
+{
+    fprintf(stderr, "ascd: bad command line syntax.\n");
+    fprintf(stderr, "Type 'ascd -help' for a list of command line options.\n");
+    exit(-1);
+} 
+
+void themes_help()
+{
+    char txt[256];
+    char key[256];
+    char r[256];
+    char d[256];
+    char arguments[256];
+    DIR *dir_fd;
+    struct dirent *dir_pt;
+    FILE *in;
+
+    fprintf(stderr, "List of AScd installed themes:\n\n");
+
+    fprintf(stderr, "Theme            Release      Description\n");
+    fprintf(stderr, "---------------- ------------ -------------------------------------\n");
+
+    sprintf(txt, "%s/Themes", THDIR);
+    if ((dir_fd = opendir(txt)) != NULL) {
+	while((dir_pt = readdir(dir_fd)) != NULL) {
+	    if (dir_pt->d_name[0] != '.') {
+		fprintf(stderr, "%-16s ", dir_pt->d_name);
+		sprintf(txt, "%s/Themes/%s/Theme", THDIR, dir_pt->d_name);
+		if (access(txt, R_OK) == 0) {
+		    if (!(in = fopen(txt, "r"))) {
+			fprintf(stderr, "cannot open Theme definition file");
+		    } else {
+			strcpy(d, "");
+			strcpy(r, "");
+			while (tes_xgets(txt, 256, in)) {
+			    fak_parse_line(txt, key, arguments);
+			    if (strcmp(key, "release") == 0) strcpy(r, arguments);
+			    if (strcmp(key, "name") == 0) strcpy(d, arguments);
+			}
+			fprintf(stderr, "%-12s %s", r, d);
+			fclose(in);
+		    }
+		} else {
+		    fprintf(stderr, "cannot find Theme definition file");
+		}
+		fprintf(stderr, "\n");
+	    }
+	}
+	closedir(dir_fd);
+    } else {
+	fprintf(stderr, "Can't read directory %s!\n", txt);
+    }
+    fprintf(stderr, "\n");
+
+    exit(0);
+}
+
+#define MAX_LINE_NAME 16
+
+void command_line_parse(int argc, char *argv[]) {
+    int i;
+    char *Argument;
+    int sw;
+
+    for (i = 1 ; i < argc ; i++) {
+	Argument = argv[i];
+
+	if ((Argument[0] == '-') || (Argument[0] == '+')) {
+	    if (Argument[0] == '-') {
+		sw = FALSE;
+	    } else if (Argument[0] == '+') {
+		sw = TRUE;
+	    }
+
+	    if ((strcmp(Argument + 1, "w") == 0) || (strcmp(Argument + 1, "withdrawn") == 0)) {
+		if (sw) withdrawn = TRUE;
+		else withdrawn = FALSE;
+	    }
+
+	    else if ((strcmp(Argument + 1, "g") == 0) || (strcmp(Argument + 1, "geometry") == 0)) {
+		if(++i >= argc) command_line_err();
+		Geometry = argv[i];
+	    }
+	    
+	    else if ((strcmp(Argument + 1, "d") == 0) || (strcmp(Argument + 1, "device") == 0)) {
+		if(++i >= argc) command_line_err();
+		cd_device = malloc(strlen(argv[i]) + 1);
+		strcpy(cd_device, argv[i]);
+	    }
+
+#ifdef MIXER
+	    else if (strcmp(Argument + 1, "mixer") == 0) {
+		if(++i >= argc) command_line_err();
+		strcpy(mixer_device, argv[i]);
+	    }
+#endif
+
+	    else if (strcmp(Argument + 1, "theme") == 0) {
+		if(++i >= argc) command_line_err();
+		strcpy(theme, argv[i]);
+	    }
+
+	    else if ((strcmp(Argument + 1, "c") == 0) || (strcmp(Argument + 1, "cue") == 0)) {
+		if(++i >= argc) command_line_err();
+		cue_time = atoi(argv[i]);
+	    }
+
+	    else if ((strcmp(Argument + 1, "i") == 0) || (strcmp(Argument + 1, "ti") == 0)) {
+		if(++i >= argc) command_line_err();
+		text_timeout = atoi(argv[i]);
+	    }
+
+	    else if ((strcmp(Argument + 1, "a") == 0) || (strcmp(Argument + 1, "autoplay") == 0)) {
+		if (sw) autoplay = TRUE;
+		else autoplay = FALSE;
+	    }
+
+	    else if ((strcmp(Argument + 1, "p") == 0) || (strcmp(Argument + 1, "autoprobe") == 0)) {
+		if (sw) autoprobe = TRUE;
+		else autoprobe = FALSE;
+	    }
+
+	    else if (strcmp(Argument + 1, "B") == 0) {
+		if (sw) blind_mode = TRUE;
+		else blind_mode = FALSE;
+	    }
+
+	    else if (strcmp(Argument + 1, "ia") == 0) {
+		if (sw) ignore_avoid = TRUE;
+		else ignore_avoid = FALSE;
+	    }
+
+	    else if (strcmp(Argument + 1, "up") == 0) {
+		if (sw) force_upper = TRUE;
+		else force_upper = FALSE;
+	    }
+
+	    else if ((strcmp(Argument + 1, "t") == 0) || (strcmp(Argument + 1, "autorepeat") == 0)) {
+		if (sw) autorepeat = TRUE;
+		else autorepeat = FALSE;
+	    }
+
+	    else if (strcmp(Argument + 1, "showtitle") == 0) {
+		show_db = TRUE;
+	    }
+
+	    else if (strcmp(Argument + 1, "showartist") == 0) {
+		show_artist = TRUE;
+	    }
+
+	    else if (strcmp(Argument + 1, "debug") == 0) {
+	        debug++;
+	    }
+
+	    else if (strcmp(Argument + 1, "mvolume") == 0) {
+		if(++i >= argc) command_line_err();
+		muted_volume = atoi(argv[i]);
+	    }
+
+	    else if (strcmp(Argument + 1, "volume") == 0) {
+		if(++i >= argc) command_line_err();
+		volume = atoi(argv[i]);
+	    }
+
+	    else if (strcmp(Argument + 1, "xf") == 0) {
+		xflaunch = TRUE;
+	    }
+
+
+	    else if (strcmp(Argument + 1, "maxvolume") == 0) {
+		if(++i >= argc) command_line_err();
+		max_volume = atoi(argv[i]);
+	    }
+
+	    else if (strcmp(Argument + 1, "minvolume") == 0) {
+		if(++i >= argc) command_line_err();
+		min_volume = atoi(argv[i]);
+	    }
+
+	    else if (strcmp(Argument + 1, "fadestep") == 0) {
+		if(++i >= argc) command_line_err();
+		fade_step = atoi(argv[i]);
+	    }
+
+	    else if (strcmp(Argument + 1, "save") == 0) {
+		save_rc_file();
+		fprintf(stderr, "Settings file saved.\n");
+		exit(0);
+	    }
+
+	    else if ((strcmp(Argument + 1, "version") == 0) || (strcmp(Argument + 1, "v") == 0)) {
+		fprintf(stderr, "%s %s (CDcontrol %s, %s) ", PACKAGE, VERSION, (char *)cd_control_version(), wm_libver_string());
+#ifdef WMK
+		fprintf(stderr, "[WINGs] ");
+#endif
+#ifdef MIXER
+		fprintf(stderr, "[mixer] ");
+#endif
+		fprintf(stderr, "\n");
+		exit(0);
+	    }
+
+	    else if (strcmp(Argument + 1, "themes") == 0) {
+		themes_help();
+	    }
+
+	    else if ((strcmp(Argument + 1, "h") == 0) || (strcmp(Argument + 1, "help") == 0)) {
+		command_line_help();
+	    } else {
+		command_line_err();
+	    }
+	} else {
+	    command_line_err();
+	}
+
+    }
+    
+} 
+
+void quick_reference(int nbr)
+{
+    char txt[127];
+    char fic[40];
+
+    if (nbr == 0) strcpy(fic, "quick*");
+    else sprintf(fic, "quick%d.gif", nbr);
+
+    sprintf(txt, "%s/Themes/%s/quick", THDIR, theme);
+    if (access(txt, R_OK) == 0) {
+	sprintf(txt, "%s %s/Themes/%s/quick/%s &", xv, THDIR, theme, fic);
+	system(txt);
+    }
+}
+
+/* ------------------------------------------------------------------------
+   dumb functions: do nothing, but they need to be present if we don't
+   wanna change anything in the WorkMan code...
+   ------------------------------------------------------------------------ */
+
+void disable_save() { }
+int get_playnew() { return autoplay; }
+void set_abtimer(int a, int b) { }
+void about_set_drivetype(char *vendor, char *model, char *rev) { }
diff --git a/ascd/ascd/misc/config.h.default b/ascd/ascd/misc/config.h.default
new file mode 100644
index 0000000..6c48908
--- /dev/null
+++ b/ascd/ascd/misc/config.h.default
@@ -0,0 +1,5 @@
+#define PACKAGE "ascd"
+#define DEFAULT_COLOR "#2FAFAF"
+#define DEFAULT_BGCOLOR "#000000"
+#define DEFAULTDEVICE "/dev/cdrom"
+#define THDIR "/usr/local/share/AScd"
diff --git a/ascd/ascd/misc/default b/ascd/ascd/misc/default
new file mode 100755
index 0000000..412d3df
--- /dev/null
+++ b/ascd/ascd/misc/default
@@ -0,0 +1,8 @@
+#!/bin/sh
+# This generates a default configuration file without
+# asking any question
+
+cp -f misc/config.h.default config.h
+cp -f misc/dirs.h.default dirs.h
+cat version.h >> config.h
+xmkmf
diff --git a/ascd/ascd/misc/dirs.h.default b/ascd/ascd/misc/dirs.h.default
new file mode 100644
index 0000000..6f1c2f9
--- /dev/null
+++ b/ascd/ascd/misc/dirs.h.default
@@ -0,0 +1,3 @@
+#define CBINDIR /usr/local/bin
+#define CMANDIR /usr/local/man/man1
+#define CTHDIR /usr/local/share/AScd
diff --git a/ascd/ascd/mixer_ext.h b/ascd/ascd/mixer_ext.h
new file mode 100644
index 0000000..4e82a37
--- /dev/null
+++ b/ascd/ascd/mixer_ext.h
@@ -0,0 +1,13 @@
+#if defined (__FreeBSD__)
+# include <machine/soundcard.h>
+#else
+# include <sys/mount.h>
+# include <linux/soundcard.h>
+#endif
+
+extern char mixer_device[];
+extern int mixer_ok;
+extern int mixer_control;
+extern int mixer_vol;
+extern int mixer_but;
+extern int mixer_old;
diff --git a/ascd/ascd/mixer_global.c b/ascd/ascd/mixer_global.c
new file mode 100644
index 0000000..9d11de0
--- /dev/null
+++ b/ascd/ascd/mixer_global.c
@@ -0,0 +1,15 @@
+#if defined (__FreeBSD__)
+# include <machine/soundcard.h>
+#else
+# include <sys/mount.h>
+# include <linux/soundcard.h>
+#endif
+
+#define DEFAULTMIXERDEVICE "/dev/mixer"
+
+char mixer_device[128]=DEFAULTMIXERDEVICE;
+int mixer_ok = 1;
+int mixer_control;
+int mixer_vol;
+int mixer_but;
+int mixer_old;
diff --git a/ascd/ascd/mixer_handlers.c b/ascd/ascd/mixer_handlers.c
new file mode 100644
index 0000000..258ff69
--- /dev/null
+++ b/ascd/ascd/mixer_handlers.c
@@ -0,0 +1,134 @@
+/* ===========================================================================
+ * AScd: mixer_handlers.c
+ * mouse events <-> integrated commands with mixer module
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+ 								   
+#include "ext.h"
+
+#ifdef MIXER
+
+int check_mixer()
+{
+    int fd;
+    if ((fd = open(mixer_device,0))) {
+        close(fd);
+    } else {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+int getvol(int control)
+{
+    int fd;
+    int status = 0;
+
+    if (mixer_ok) {
+        if ((fd = open(mixer_device,0))) {
+            ioctl(fd, MIXER_READ(control), &mixer_vol);
+            mixer_vol = mixer_vol >> 8;
+            close(fd);
+	    return mixer_vol;
+        }
+    }
+    return 0;
+}
+
+void setvol(int control, int setto, int maximum)
+{
+    int fd;
+
+    if (setto > maximum) setto = maximum;
+    if (setto < 0) setto = 0;
+
+    if ((fd = open(mixer_device, 0))) {
+        mixer_vol = ((float)setto / (float)maximum) * 100;
+        mixer_old = mixer_vol << 8;
+        mixer_vol = mixer_old | mixer_vol;
+        ioctl(fd, MIXER_WRITE(control), &mixer_vol);
+        close(fd);
+    }
+}
+
+void mixer_event_handle(int event, XEvent Event)
+{
+    int value;
+
+    if (!mixer_ok) return;
+
+    switch (event) {
+    case FAK_MIXER_SET:
+	if ((thdata[but_current].xpm.attributes.width > 0) && (max_volume > 0)) {
+	    if (thdata[but_current].type == FAK_MIXER_BAR) {
+		value = (int)((float)(Event.xbutton.x - thdata[but_current].x) / (float)thdata[but_current].xpm.attributes.width * 100.0);
+	    } else if (thdata[but_current].type == FAK_VMIXER_BAR) {
+		value = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * 100.0);
+	    } else {
+		value = (int)((float)(Event.xbutton.y - thdata[but_current].y) / (float)thdata[but_current].xpm.attributes.height * 100.0);
+		value = 100 - value;
+	    }
+	    setvol(thdata[but_current].arg, value, 100);
+	    redraw = TRUE;
+	    fak_redraw();
+	}
+	break;
+    case FAK_MIXER_0:
+	setvol(thdata[but_current].arg, 0, 100);
+	redraw = TRUE;
+	fak_redraw();
+	break;
+    case FAK_MIXER_50:
+	setvol(thdata[but_current].arg, 50, 100);
+	redraw = TRUE;
+	fak_redraw();
+	break;
+    case FAK_MIXER_75:
+	setvol(thdata[but_current].arg, 75, 100);
+	redraw = TRUE;
+	fak_redraw();
+	break;
+    case FAK_MIXER_100:
+	setvol(thdata[but_current].arg, 100, 100);
+	redraw = TRUE;
+	fak_redraw();
+	break;
+    default:
+	break;
+    }
+
+    redraw = TRUE;
+    fak_singlemask(but_current);
+}
+
+#endif
diff --git a/ascd/ascd/themes/default.tar b/ascd/ascd/themes/default.tar
new file mode 100644
index 0000000000000000000000000000000000000000..1c501b84dbb2ba842764ead3870e310bdfa8c23a
GIT binary patch
literal 40960
zcmeHQYj0aelJ(cguV7*)!PtqzH*FXW{E+o+7yEI6#USuFo|#}WNnks`0E_+aTjx>T
zeP5(xi;k4=imI;1sZ-T`?`@Hk1fISA+sj{neD}?*BObH$e7D)mZp~)1-FD4!vDO$a
z9P`<1yIZXnyY*(Xo6Q!BNVqlI9I>ZD{p;IzFW<~&x4wV<(+_Wpcc}dO_Vt^6@nOb|
z@SOSl|Mlgof8PK1FaJ75(~J3h>+?N!{<oX;0_T6Rop0v5U0e%`<#w^YHJcx!nLg(q
z{QQ4&clO`^^&hjl-+U7H!Mh(`&0hT@Zil-wTo2#H_3%G`nLVEU?<b#awzGIFW{c0h
z`sB0OpI*)W^XH#lQ`a4QX?e=M_>!I0>a<o<D{#6trdDR?KdsepO{P4jwVGO)q5rg2
z!!?=moYrb;WrqIKS`F7^%5z$)sg)V}Pir+?lPS*!t=0cM`lSDQ^P1W1>;Lcm`tr>&
z-UB%B`hT`rZ~6LvGv97jyLc~Pv05(|r`P`<<5JoC`aiCR_C0|0a<+-D!CL_L<L!Xk
zn_YZuakt!qxmd>68s-z2@y^bA&ggBJFXm_GFUWKM!UwqS-+OYeK0*tV-xAfhEp#}h
z4rJz$X!#0fWe*^05kU0q3m>0d>rJ3@|G)bA*Pq_KzVbcb!2Q2iEY^Ad&$ru6-2dy<
zI^O?1z5jRo9boVM@9Uwn2drj``E31xo4|!{`uq0}-pF|qYaH$panDXa-izIv4L5_m
zdSyS1jV=ink{b?Yv?npy1F{EII0P^BAm4X1>_LyTDaqjV-sO&eAKlano&En`Z~y)q
zJpWs)m$B!^?|(M)&2F_=^7-H9^!uNWa!Kvo|KoaS_kR2WXmRCF0QYS|j&alS&gCJW
zvK`v{lHF<C#*{11v_wQ(o@xl`y3=V{u#Un8SLu0ww(;Cn!9u&_R^EY@v|WNy7jA~e
zHgb@4^F*I@ohWBt_7amna~rsjtz^&y$rHZJO|CYj(R+z7J>LsIKkotmU_Zz@`~SN)
zNBJv&1JD0w@kbx^`G35~jMx9W)BgWaw*Omu_kUgw?N5LOpZ&kzTLASnKi>kV?+A2W
z4B%;JvtQ^*@x9xzA^5>($wLI<aX?lCeYcB4kVMpkiU39B2!vCBxYjS@-}_>z_y3i@
z>@PhXy8pM^_?s{N{m&xaCW`xixn3<#um8&e9#NUT|GRJfEoO1+?<QXMpReX;^M|~3
z@$TOC{K5Hl2b1spJ=?6Gt!VPbUoD=WJ>+|TcK{CP`T88-&taaemd_S6zks=Z@of2E
zj_6&Oi`9#>r!4ds=H~3_Vn?d4VLn}K&ek*^!F;h?Jl9*0-@;rj&z?Vg4yvzUKA1n4
zZ)koA^Z9!9bol_$Kf|1_&!0cz+n1lhTrZ!!c%bN?@7}%p?YHry;nPpQ_~P;7$FX62
z_0^p_cfS7m>nBg1eEH?)pMU=7QEX|X5;)KR2O8Ke_)!P^Eto%@U=C`5uW~2K`~TIO
zpWnVc#*4qWb`RbEtEK+_5AXkPS9t$#HDAt8_x}-Z`$9c+|MPlifAsI-h2PZu|8(<o
z`?S9I|8Nyw^}YWW=lH_?!25r<#%~H>^8T;CH{kuhI>#5Hep&z(PwL~TJt=PAeiAny
z)9*ZquRBoVro-_&{TW?O))ADv|G)p???1dd(gv{q{$K94JO2FV-+wQlFILO->HVK0
zob4gy)cybR?W^y<(?!wl0`Z&K)$Adj3*feE{|*?x1kAq)=5GL3^YioP?VWu89&YJ-
z8gzHVb1wuV)g?#cRA^L+f>;Ug1t|>><0-vdMNfw?qqKH*kYZ61yUtQEqDZNN21*Y^
z(&NmcC#Iyz0T{{P*K+NV#2`UMnHz!~aO?@-nxipE!BFEg)Sh9Wp&C&yfiDxNZS)8y
z13lceHQUv|nbm<Yzw^Y)p(QE6?z~8Vlh0g|Ni?#(2#jn=h}5M<w{7V%*oZ{9MQv;t
zzL&F4gryVG;`0Jlpz_#Hhv=lXx#`acil9u|94+L7ls45*hm$?KWe(022aTNRc>kr_
zwlJU~Vse+EE#m@&XF_Q|6zsIbC!rUO7%oMwhO(7gm@+FVld6sZ1sa3L(2EAQupVG5
zDp`rTumUf5F@@iW<gwXSN&c=6Sx5{S06q${2!~P`Lx1ZbBUn?ZI$dzPFx8IU2~~K(
z*cC(7#BxE|a307IvXPCdHp3y`0jBF7<|K;fT`kQFp|;T`qy?$V5h0;DcL6D-!i!C(
zTEf|81iHShIY^{4>B^Rxcn|;qPaVt`9{7DorEL8j&H=_zR}mo$6A#$rFEpdbwz@Ws
zY>{J0MiCi`Q@3dR-qPjiV>%3()Pgvb4A98AvFP{~ioFxe&Z~UNO}ZEpl`hSsO8DDv
zM1&2=0xV^pg2H#RoI|J1k388ySYji+(WI+7L#_|6A{aDNBg25LuNqTEl|g`1f2SW=
zOid{OQC6LiR%rxF9!^RR=?Z@`AZ!f0K3G&3HeWEw2Ap-(H;)oi#)xR=ky&JbP<}|j
zkfA|IG;gTjTw-m@OuYP@utZK6bLMG44)IK`69-Lk31E-BC6?)dZhRz4WQ@M7REm;L
zCfA|^tuhKOTfGhCAU)_g3D;6n^7JK`3A4~_re%lORr;KXfj_=cqPV2@CG63IxucQd
zWOZAuUH}~hs}~M*HrPT1^xRdMSm!V-HwiQ+1k>qRx*&5^OKYGkEQyxfgoS_!YOmoG
zs+4F<@~~YACJ9I?v;&O&7@#ZW;_SwaMOW<?yygk8a-opG5oQvmOxgs4AK7D)ZVO?E
z3RMp!6|=NlJnou<nsh&I#XJRX+TPT&9HZ{PO;J2NIC9_!;jYB#D3Lc&a6($cT<tPZ
z&nN7hCFYEWMtDP|ks^_ZAis(<;3Wwm<e*DJ_{^<Q^E3`(SiE~iSTdUm5HlxbH#h5|
zHC`8yEQV>x$q+S-aoc1wp%&qQBx9LCreiYYk?RbU+lShjS1+J2FnGjFk(bR^W;$CF
zympRnp{7zfpp_tnx#>bR(`B?XW-P}xIjb~17cLd;XK`IPORt3SoHyvw&i2BtOsbuj
z(n$cSPdaRU(Pl;UA|&XuH;)XUfkH=wgwS2tStb%@88Wy8bovXMq9KzG5mdFqeHU0y
zaYIUARs(RZ=;Tj);V)%bkPKO#n42pw7oNtPqE3j{pY!sXK*y<8Mor7a7!hVl6||ky
z#6pGT$_*R@iZ}>EuC{ijieN&D$^>Ozk=p2{X3dVyWDrk~7r&JK$OPx&fxKzrfUXso
z!1tT>VrlMC_wa-Cb#bRtB9d(F7OH$otrcer8Yd(GCJv4=TB%qKLd)-(lW|2d-j2eO
zt*Fe((h51&;R%CN;0T!W(oehf!66$`xmpA-g);;bMj6&iN@mq)w|>@vw>=6^rxn!$
z4snOlNtc}&4v1Rmf|UZ0lQ?D){R&DdYTboPF5Es+&Cz#5hHuuW>2fMX$cK5*rCWj1
za)fs?qK<pYHP4ReA)rEwp$p82MwAatd#m(-W7i4)ewjsjovNm!AS2Qj6bAKiKt^~P
zX?~U5i|kfmLdn9kxl*m{_-Y_D7#j9Vk!JJbDPDe)j{G`_eM#_~kOm#JhwQ#LXFw!Q
zb2@N_Lx%*X2BFfgs1pcbrvw3$N`GWPl!S>Mbyy2Nx{`o?B2|&8q>ptg$hwA!7m*q%
z)M`r<<^Te<$~QUDML6x2O4CurNR(_$kYSl1vXH|>G3eo{>>xeBbU9tvbS12U**Maq
zXXhj$8lm6gp*dr*;@i{4Jn6Sg3vNLa9jr%7z#u3`vG(h_+0T<0jMPa9Rz>zoR);{r
zKn|p5gka7tbykRY-`_{FeG*S2wUWpT3=Jh5kgUF^MG__}sZ{`~W`uGu=%11`m}!IM
z0FCDe%86&8Ac})^q;@3&!{E@<&AQ>%^_bqUi=n5t&;?MG9$?(O9D1DN=6R=PO}}o&
ztDV$X!7*P{b@>lzWrF!?r*vVq%Xf46j0-oDz(XYvPfU#ax6mX#ja&i!0Q*fBW7xvb
zGiWf+cRBF`#HgdBjYdz-D_Vh(Wgc<47$ZLA)2Qjr-X~|8?c+zTL;vU@`-t&M{@bPd
z{~ybv{0G=&{{LgWT;cyeR;T~}8vd`wF+9in|Bv%H^sk$FxANN6>p#1AMTcNSKB)|3
z-rkW&ZA?t{E5Eu`;dPrpd@2Y%9ZcBI?o?3+RH+zK2YtS_wja0*3rUa4LHb3oF(jDK
z6WL~U^pdQikhM-&{X@axZA|AysBs!-&oEGkMwLt8SsnP=Mvrnb(8FC@vt12bGdeiu
zP-WBalddHx!0x;RY|p+J1ka=_f@Mu#;b|(;XIr{6IaqfAW5cOFb;jCGhWNaURlAbr
zOk2)AMrB+}K+a{Vik8w*PI`#!a84|YPl=oL;6`I;{jK3*4W6yLe@81#AtI&1A&o;;
z`%;N+QCVUsFabnLQqkpUB`bGDgn_`|G4!IrExZTVib{6L3OcjMoJfyl2t^Oo2Ni7t
zlB2-j4+y8MSqnKO)y-5?elmhJm8#PPcQ!K6JE00Mc)Mb#8c8lH8qNbLLN>Bd)n+*4
zJHT|^!<<ABc{O&=Am#u_8|_YBQO`Au#ukuLD%o(^8J=0T8G){EYYqx4gECuc;<}cz
z1q258EXlFqNqQwY0B3`7MOTSP#9-{NLIOg^ke$UK8H$#pB%_E7#i&~}esAgW^f4WV
zOlm<vJ-{R`V&hvVeq<rbz{;m}$@~}-m98>NEAY49a0?rf1z5^H1%>ZsIfqW2A9=Ea
zu*61sqe)kFhFl+BMKEZlMuq`fUp1zTDuV#2{!Tx#n3_@mqO3Y2t<ngVJe-ss(iQ$>
zK-d^~eXyu7Y`$QU4LIwnZyqJ4j1ke!BeTc=q5P15Awz?bXx>o4xy0I*nRxj*VTqhD
z=FHQ89O9W=Ck~q862KmLODxj^-S|k9$QXTDsT3uhOs+)-T4fYmwt5@NL3+@060W7D
z<mpQ=6K0{=Ov?_ltMoY&1Aly@L~%*)OW30ab4Me^$?CRRy#P82RxcdrY_Nq2=((#h
zvCd&wZW3rt2&U7sbV25-mexR7SQ0I{2@3%e)Lz3WR4LJz<YBuKOcIb(Xa^YkF+f+$
z#o3J;i>}%)c+C@F<w7BWBg`aBnY0N8KeERp-4?<S6{;ReDrRZ9c-%DyHR*obig^m&
zw7sckIY!-oo1%DlaOA)d!d;2eQ6g`m;Doe>x!Pr-o=@00OUxM$jqrv_BSj(+L4Fl!
zz)KQB$U&Ec@R?hq=4l+nuz2^3uw*tBAZAX=Zf@2^YrHNZSq#&XlObvv<F?6WLM_4p
zNyajPOvhx(Bi9)yw-2>5uU<f5VDN~UA}^b-%yhOUc<mhDLQSP|Kr2BCbJK-trpst&
z%vg?Xa#m@2E?g?w&*HjpmR<?tId9OVo$ZBPnN&M5rIP?upLE#zqRooxMM%(RZyp&y
z1BH$V38A~PvrHt+GGuTG==2vhMMEYXBB*MI`!2Aa;)ayKtOnp*(aE3q!e7d=AQ`ee
zF*jFWE<BAnMV%0@Kj-B&fsRwHjGC5-F(S;ADrh^YiG>Qwl^Zw+6mbxSTy5=46~TlQ
zl?lqcBDK*?&6*vZ$snE}FMcWekqOSl19{WL0bMIFf$ul%#nRlP?%@aN>*7wQL?qeT
zEmZlGS}V>LG)_nWOdK3#v{JDegqGhmC*z7_yd8xlTTz*nr4@3l!xILlz!5O#rJr`|
zgF`l?a<vFv3TFr=j54g3l+3EpZvCtSZ+jG;PAjSh9O4e8lP)_m91ykA1uF$0CvnUq
z`W2K^)Vd3oT)2ItnxpTA4BxC#)8$l(kPq{qOSb~2<p}R)L>>2(Yn~m`LqLTVLl>A4
zjVK?Q_EzZu$F39p{W6R6I#o?cK}Mu6C=BZ3fQ;}o()=p97ul`Cgp!46bER6@@zp?R
zFf{C!BF*N<Q@s2p9r<+<`;y=}Aq_fc57~Wh&VWdq=5*i;hYkr&4ML?~Q6~_>P6+}g
zmHx<pC<zli>aZ4kbR_}(M5-cFNgwN0kaZ0cFCsNksMVGz%mD;ym2YyQi*VX4m8PSL
zkto@iAj2|2WFd!%V$j1?*+F`M>2kWT=}K4yvvH(J&(29iG(x|}LvzMr#kZ%8dD3s0
z7Tkg;I#`dEfI(1>V(r&;v!5q17^#yItcvWFtPX*KfgDKB2*I3P>Z}m)zQ2!T`y`%5
zY9)~w7#d1AAX$A+izG}|QmX(|%?RaS&_5+>Fw+Lh0UFN{loQWFK@<n;NbO1lhQXny
zn{~sj>oL7y7eh~Pp$niWJ;1nmIrKQk&GSypntt7kS39Y(f@8j@>hd4b$^`S(PU*sI
zm+$8C85eFQfrm;Uo|qW-Z=p$g8o2`c0rs0N#;}E<XV74t?{eY?h*3vL8;zcvSF{2n
z%RJ(8F-CmKr%}_Ly-&_G+sBVwhyKw+_7UTi{{P26UjO*ZQU3WO{%K+V|9{M8@y{Ok
z{IBJFzFhC-@mWBN<z}}#eg4le{@<g|IraY^xE|UM{<y@)|J2X>;m3c~PXoH_$Nylv
z!SjSC@##PDfQk?6yq6#U6Q8<OKK|$4>EnO;r}jd2{{Po6FMoRd<B>Li!}ovv{IA#q
zaR2W%r_cYp(E0mN(A53U>)|~&fO^Ms1E~AKWj6r)n8#foui)^>zS;odQ})UR5Fh__
a`uwjCy#!AArxiG@z-a|eEAR(gf&T|B=m%E-

literal 0
HcmV?d00001

diff --git a/ascd/ascd/themes/themes-manual.ps.gz b/ascd/ascd/themes/themes-manual.ps.gz
new file mode 100644
index 0000000000000000000000000000000000000000..21ff8603776338e83f641413641fa5cf579b4f0e
GIT binary patch
literal 82472
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
ae|qX|UjOUg{O`Yg_x}LGWdqWQGzI_>A=)nh

literal 0
HcmV?d00001

diff --git a/ascd/ascd/themes/themes.tar b/ascd/ascd/themes/themes.tar
new file mode 100644
index 0000000000000000000000000000000000000000..a02586c51ca1b05d83f05c4eff2ecaaa156ee9c4
GIT binary patch
literal 92160
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
HO%MDZM36Q8

literal 0
HcmV?d00001

diff --git a/ascd/ascd/version.h b/ascd/ascd/version.h
new file mode 100644
index 0000000..d651757
--- /dev/null
+++ b/ascd/ascd/version.h
@@ -0,0 +1 @@
+#define VERSION 		"0.13.2"
diff --git a/ascd/ascd/wings_ext.h b/ascd/ascd/wings_ext.h
new file mode 100644
index 0000000..3d830fb
--- /dev/null
+++ b/ascd/ascd/wings_ext.h
@@ -0,0 +1,75 @@
+extern WMScreen *scr;
+
+/* the main window */
+
+extern WMWindow *win;
+
+extern WMButton *b_title;
+
+extern WMPopUpButton *pop, *pop2, *pop3;
+
+extern WMButton *b_autoplay;
+extern WMButton *b_autorepeat;
+extern WMButton *b_ignoreavoid;
+extern WMTextField *b_device;
+
+extern WMSlider *b_volume;
+
+extern WMButton *b_scroll;
+extern WMButton *b_artist;
+extern WMButton *b_upper;
+
+extern WMTextField *b_cuetime;
+extern WMTextField *b_fadestep;
+
+extern WMTextField *b_minvol;
+extern WMTextField *b_maxvol;
+extern WMTextField *b_mutedvol;
+
+extern WMButton *b_mode1;
+extern WMButton *b_mode2;
+extern WMButton *b_mode3;
+extern WMButton *b_mode4;
+
+extern WMButton *b_save;
+extern WMButton *b_quit;
+extern WMButton *b_close;
+extern WMButton *b_about;
+
+extern WMButton *b_help;
+
+/* the database window */
+
+extern WMWindow *dbwin;
+
+extern WMTextField *db_artist;
+extern WMTextField *db_title;
+extern WMTextField *db_track;
+extern WMButton *db_avoid;
+extern WMButton *db_playauto;
+
+extern WMSlider *db_volume;
+
+extern WMButton *db_close;
+extern WMButton *db_prev;
+extern WMButton *db_next;
+
+extern WMWidget *db_tlabel;
+
+/* the 'about' window */
+
+extern WMWindow *aboutwin;
+extern WMWidget *about_th1;
+extern WMWidget *about_th2;
+extern WMWidget *about_th3;
+extern WMWidget *about_th4;
+extern WMWidget *about_th5;
+extern WMWidget *about_th6;
+
+/* misc */
+
+extern int en_vue;
+extern int db_en_vue;
+extern int about_en_vue;
+extern int pistes;
+int db_curtrack;
diff --git a/ascd/ascd/wings_global.c b/ascd/ascd/wings_global.c
new file mode 100644
index 0000000..cf85ac1
--- /dev/null
+++ b/ascd/ascd/wings_global.c
@@ -0,0 +1,79 @@
+#include <dirent.h>
+#include <WINGs.h>
+
+WMScreen *scr;
+
+/* the main window */
+
+WMWindow *win;
+
+WMButton *b_title;
+
+WMPopUpButton *pop, *pop2, *pop3;
+
+WMButton *b_autoplay;
+WMButton *b_autorepeat;
+WMButton *b_ignoreavoid;
+WMTextField *b_device;
+
+WMSlider *b_volume;
+
+WMButton *b_scroll;
+WMButton *b_artist;
+WMButton *b_upper;
+
+WMTextField *b_cuetime;
+WMTextField *b_fadestep;
+
+WMTextField *b_minvol;
+WMTextField *b_maxvol;
+WMTextField *b_mutedvol;
+
+WMButton *b_mode1;
+WMButton *b_mode2;
+WMButton *b_mode3;
+WMButton *b_mode4;
+
+WMButton *b_save;
+WMButton *b_quit;
+WMButton *b_close;
+WMButton *b_about;
+
+WMButton *b_help;
+
+/* the database window */
+
+WMWindow *dbwin;
+
+WMTextField *db_artist;
+WMTextField *db_title;
+WMTextField *db_track;
+WMButton *db_avoid;
+WMButton *db_playauto;
+
+WMSlider *db_volume;
+
+WMButton *db_close;
+WMButton *db_prev;
+WMButton *db_next;
+
+WMWidget *db_tlabel;
+
+/* the 'about' window */
+
+WMWindow *aboutwin;
+WMWidget *about_th1;
+WMWidget *about_th2;
+WMWidget *about_th3;
+WMWidget *about_th4;
+WMWidget *about_th5;
+WMWidget *about_th6;
+
+/* misc */
+
+int en_vue = FALSE;
+int db_en_vue = FALSE;
+int about_en_vue = FALSE;
+int pistes = 0;
+
+int db_curtrack = 1;
diff --git a/ascd/ascd/wings_main.c b/ascd/ascd/wings_main.c
new file mode 100644
index 0000000..37cbb25
--- /dev/null
+++ b/ascd/ascd/wings_main.c
@@ -0,0 +1,1070 @@
+/* ===========================================================================
+ * AScd: the AfterStep and WindowMaker CD player
+ * gui_wings.c: window handling using WINGs toolkit
+ * ===========================================================================
+ * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    This product includes software developed by Denis Bourez & Rob Malda
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+ * POSSIBILITY OF SUCH DAMAGE.
+ * ===========================================================================
+ */
+
+#include <dirent.h>
+#include <WINGs.h>
+#include "ext.h"
+
+/*int old_track = 0;*/
+
+void close_win(WMWidget *self, void *data) {
+    WMCloseWindow(win);
+    en_vue = FALSE;
+    muted_volume = atoi(WMGetTextFieldText(b_mutedvol));
+    min_volume = atoi(WMGetTextFieldText(b_minvol));
+    max_volume = atoi(WMGetTextFieldText(b_maxvol));
+    cue_time = atoi(WMGetTextFieldText(b_cuetime));
+    fade_step = atoi(WMGetTextFieldText(b_fadestep));
+}
+
+void close_aboutwin(WMWidget *self, void *data) {
+    WMCloseWindow(aboutwin);
+    about_en_vue = FALSE;
+}
+
+void close_dbwin(WMWidget *self, void *data) {
+    char txt[127];
+    char txt2 [127];
+
+    WMCloseWindow(dbwin);
+    strcpy(cd->artist, WMGetTextFieldText(db_artist));
+    strcpy(cd->cdname, WMGetTextFieldText(db_title));
+    if (strlen(WMGetTextFieldText(db_track)) > 0) {
+	cd->trk[db_curtrack - 1].songname = malloc(strlen(WMGetTextFieldText(db_track)) + 1);
+	strcpy(cd->trk[db_curtrack - 1].songname, WMGetTextFieldText(db_track)); 
+    }
+
+    save();
+    load();
+
+    if (cd->artist != NULL) {
+	strcpy(txt, cd->artist);
+	if (strcmp(txt, "") == 0) strcpy(txt, "unknown artist");
+    } else {
+	strcpy(txt, "unknown artist");
+    }
+	    
+    strcat(txt, "\n");
+    
+    if (cd->cdname != NULL) {
+	strcpy(txt2, cd->cdname);
+	if (strcmp(txt2, "") == 0) strcpy(txt2, "unknown album");
+    } else {
+	strcpy(txt2, "unknown album");
+    }
+    
+    strcat(txt, txt2);
+    WMSetButtonText(b_title, txt);
+    
+    db_en_vue = FALSE;
+}
+
+void quit_ascd(WMWidget *self, void *data) {
+    exit(0);
+}
+
+void save_cb(WMWidget *self, void *data) {
+    muted_volume = atoi(WMGetTextFieldText(b_mutedvol));
+    min_volume = atoi(WMGetTextFieldText(b_minvol));
+    max_volume = atoi(WMGetTextFieldText(b_maxvol));
+    cue_time = atoi(WMGetTextFieldText(b_cuetime));
+    fade_step = atoi(WMGetTextFieldText(b_fadestep));
+    cd_device = WMGetTextFieldText(b_device);
+    save_rc_file();
+}
+
+void track_select(WMWidget *self, void *data) {
+    show_db_pos = 0;
+    direct_track =  WMGetPopUpButtonSelectedItem(self) + 1;
+    cd_control(DIRECTTRACK);
+    old_track = 0;
+    if (cur_cdmode == WM_CDM_STOPPED) cd_control(PLAY);
+}
+
+void theme_pick(WMWidget *self, void *data) {
+    strcpy(theme, WMGetPopUpButtonItem(self, WMGetPopUpButtonSelectedItem(self)));
+    fak_load_theme(theme, TRUE);
+    fak_maskset();
+}
+
+void help_cb(WMWidget *self, void *data) {
+    quick_reference(0);
+}
+
+void mode1_cb(WMWidget *self, void *data) {
+    if (WMGetButtonSelected(b_mode1)) {
+	WMSetButtonSelected((b_mode2), FALSE);
+    } else {
+	WMSetButtonSelected((b_mode2), TRUE);
+    }
+
+    if (WMGetButtonSelected(b_mode1)) {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 0;
+	else time_mode = 1;
+    } else {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 2;
+	else time_mode = 3;
+    }
+}
+
+void mode2_cb(WMWidget *self, void *data) {
+    if (WMGetButtonSelected(b_mode2)) {
+	WMSetButtonSelected((b_mode1), FALSE);
+    } else {
+	WMSetButtonSelected((b_mode1), TRUE);
+    }
+
+    if (WMGetButtonSelected(b_mode1)) {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 0;
+	else time_mode = 1;
+    } else {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 2;
+	else time_mode = 3;
+    }
+}
+
+void mode3_cb(WMWidget *self, void *data) {
+    if (WMGetButtonSelected(b_mode3)) {
+	WMSetButtonSelected((b_mode4), FALSE);
+    } else {
+	WMSetButtonSelected((b_mode4), TRUE);
+    }
+
+    if (WMGetButtonSelected(b_mode1)) {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 0;
+	else time_mode = 1;
+    } else {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 2;
+	else time_mode = 3;
+    }
+}
+
+void mode4_cb(WMWidget *self, void *data) {
+    if (WMGetButtonSelected(b_mode4)) {
+	WMSetButtonSelected((b_mode3), FALSE);
+    } else {
+	WMSetButtonSelected((b_mode3), TRUE);
+    }
+
+    if (WMGetButtonSelected(b_mode1)) {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 0;
+	else time_mode = 1;
+    } else {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 2;
+	else time_mode = 3;
+    }
+}
+
+void autoplay_cb(WMWidget *self, void *data) {
+    if (autoplay) autoplay = 0;
+    else autoplay = 1;
+
+    if (WMGetButtonSelected(b_mode1)) {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 0;
+	else time_mode = 1;
+    } else {
+	if (WMGetButtonSelected(b_mode3)) time_mode = 2;
+	else time_mode = 3;
+    }
+}
+
+void autorepeat_cb(WMWidget *self, void *data) {
+    if (autorepeat) autorepeat = 0;
+    else autorepeat = 1;
+}
+
+void ignoreavoid_cb(WMWidget *self, void *data) {
+    if (ignore_avoid) ignore_avoid = 0;
+    else ignore_avoid = 1;
+}
+
+void upper_cb(WMWidget *self, void *data) {
+    if (force_upper) force_upper = 0;
+    else force_upper = 1;
+}
+
+void scroll_cb(WMWidget *self, void *data) {
+    if (show_db) show_db = 0;
+    else show_db = 1;
+}
+
+void artist_cb(WMWidget *self, void *data) {
+    if (show_artist) show_artist = 0;
+    else show_artist = 1;
+}
+
+void volume_cb(WMWidget *self, void *data) {
+    if (cur_cdmode != WM_CDM_EJECTED) {
+	volume = WMGetSliderValue(self);
+	cd_volume(volume, 10, max_volume);
+	redraw = TRUE;
+    }
+}
+
+/* --------------------------------------------------- */
+
+void create_about_window(WMScreen *scr) {
+    char txt[256];
+    WMWidget *lb1, *lb2, *lb3, *lb4, *lb5, *lb6;
+    WMFont *font;
+
+    aboutwin = WMCreateWindow(scr, "AScd-about");
+    WMSetWindowCloseAction(aboutwin, close_aboutwin, NULL);
+    WMSetWindowTitle(aboutwin, "AScd Info");
+    WMResizeWidget(aboutwin, 350, 170);
+
+    lb1 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb1, 35, 20);
+    WMMoveWidget(lb1, 10, 10);
+    font = WMBoldSystemFontOfSize(scr, 12);
+    if (font) {
+	WMSetLabelFont(lb1, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(lb1, "AScd");
+
+    lb2 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb2, 200, 20);
+    WMMoveWidget(lb2, 50, 10);
+    sprintf(txt, "release %s", VERSION);
+    WMSetLabelText(lb2, txt);
+
+    lb3 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb3, 280, 40);
+    WMMoveWidget(lb3, 50, 30);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(lb3, font);
+	WMReleaseFont(font);
+    }
+    sprintf(txt, "(c) 1996-1999 Denis Bourez and Rob Malda\nContact: [email protected]\nhttp://worldserver.oleane.com/rsn/ascd-en.html");
+    WMSetLabelText(lb3, txt);
+
+    lb5 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb5, 60, 20);
+    WMMoveWidget(lb5, 10, 70);
+    font = WMBoldSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(lb5, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(lb5, "Ctrl:");
+
+    lb4 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb4, 260, 20);
+    WMMoveWidget(lb4, 50, 70);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(lb4, font);
+	WMReleaseFont(font);
+    }
+    sprintf(txt, "CD Control %s + %s", cd_control_version(),  wm_libver_string());
+    WMSetLabelText(lb4, txt);
+
+    lb6 = WMCreateLabel(aboutwin);
+    WMResizeWidget(lb6, 60, 20);
+    WMMoveWidget(lb6, 10, 85);
+    font = WMBoldSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(lb6, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(lb6, "Theme:");
+
+    about_th1 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th1, 280, 12);
+    WMMoveWidget(about_th1, 50, 89);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th1, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th1, "A");
+
+    about_th2 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th2, 280, 12);
+    WMMoveWidget(about_th2, 50, 101);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th2, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th2, "B");
+
+    about_th3 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th3, 280, 12);
+    WMMoveWidget(about_th3, 50, 113);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th3, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th3, "C");
+
+    about_th4 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th4, 280, 12);
+    WMMoveWidget(about_th4, 50, 125);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th4, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th4, "D");
+
+    about_th5 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th5, 280, 12);
+    WMMoveWidget(about_th5, 50, 137);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th5, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th5, "E");
+
+    about_th6 = WMCreateLabel(aboutwin);
+    WMResizeWidget(about_th6, 280, 12);
+    WMMoveWidget(about_th6, 50, 149);
+    font = WMSystemFontOfSize(scr, 10);
+    if (font) {
+	WMSetLabelFont(about_th6, font);
+	WMReleaseFont(font);
+    }
+    WMSetLabelText(about_th6, "F");
+
+
+
+    WMRealizeWidget(aboutwin);
+}
+
+void about_window(WMScreen *scr) {
+    char txt[256];
+
+    if (! about_en_vue) {
+	sprintf(txt, "%s %s", th_name, th_release);
+	WMSetLabelText(about_th1, txt);
+	WMSetLabelText(about_th2, th_author);
+	WMSetLabelText(about_th3, th_email);
+	WMSetLabelText(about_th4, th_url);
+	WMSetLabelText(about_th5, th_comment);
+	sprintf(txt, "Faktory: %dx%d (panels: %d Buttons: %d)", backXPM.attributes.width, backXPM.attributes.height, panels, but_max);
+	WMSetLabelText(about_th6, txt);
+
+	WMRealizeWidget(aboutwin);
+	WMMapSubwidgets(aboutwin);
+	WMMapWidget(aboutwin);
+	about_en_vue = TRUE;
+    } else {
+	WMCloseWindow(aboutwin);
+	about_en_vue = FALSE;
+    }
+}
+
+/* --------------------------------------------------- */
+
+void db_prev_cb(WMWidget *self, void *data) {
+    char txt[256];
+
+    if (cur_cdmode == WM_CDM_EJECTED) return;
+
+    strcpy(cd->artist, WMGetTextFieldText(db_artist));
+    strcpy(cd->cdname, WMGetTextFieldText(db_title));
+    if (strlen(WMGetTextFieldText(db_track)) > 0) {
+	cd->trk[db_curtrack - 1].songname = malloc(strlen(WMGetTextFieldText(db_track)) + 1);
+	strcpy(cd->trk[db_curtrack - 1].songname, WMGetTextFieldText(db_track));
+    }
+    save();
+    load();
+
+    if (db_curtrack == 1) db_curtrack = cur_ntracks;
+    else db_curtrack--;
+    sprintf(txt, "Tk %d:", db_curtrack);
+    WMSetLabelText(db_tlabel, txt);
+
+    if (cd->trk[db_curtrack - 1].songname != NULL) {
+	sprintf(txt,  cd->trk[db_curtrack - 1].songname);
+    } else {
+	strcpy(txt, "");
+    }
+    WMSetTextFieldText(db_track, txt);
+
+    if (cd->trk[db_curtrack - 1].avoid) {
+	WMSetButtonSelected(db_avoid, TRUE);
+    } else {
+	WMSetButtonSelected(db_avoid, FALSE);
+    }    
+    
+    WMRealizeWidget(dbwin);
+}
+
+void db_next_cb(WMWidget *self, void *data) {
+    char txt[256];
+
+    if (cur_cdmode == WM_CDM_EJECTED) return;
+
+    strcpy(cd->artist, WMGetTextFieldText(db_artist));
+    strcpy(cd->cdname, WMGetTextFieldText(db_title));
+    if (strlen(WMGetTextFieldText(db_track)) > 0) {
+	cd->trk[db_curtrack - 1].songname = malloc(strlen(WMGetTextFieldText(db_track)) + 1);
+	strcpy(cd->trk[db_curtrack - 1].songname, WMGetTextFieldText(db_track));
+    }
+    save();
+    load();
+
+    if (db_curtrack == cur_ntracks) db_curtrack = 1;
+    else db_curtrack++;
+    sprintf(txt, "Tk %d:", db_curtrack);
+    WMSetLabelText(db_tlabel, txt);
+
+    if (cd->trk[db_curtrack - 1].songname != NULL) {
+	sprintf(txt,  cd->trk[db_curtrack - 1].songname);
+    } else {
+	strcpy(txt, "");
+    }
+    WMSetTextFieldText(db_track, txt);
+
+    if (cd->trk[db_curtrack - 1].avoid) {
+	WMSetButtonSelected(db_avoid, TRUE);
+    } else {
+	WMSetButtonSelected(db_avoid, FALSE);
+    }
+    
+    WMRealizeWidget(dbwin);
+}
+
+void db_avoid_cb(WMWidget *self, void *data) {
+    if (cur_cdmode == WM_CDM_EJECTED) return;
+    if (cd->trk[db_curtrack - 1].avoid) {
+	cd->trk[db_curtrack - 1].avoid = 0;
+    } else {
+	cd->trk[db_curtrack - 1].avoid = 1;
+    }
+    save();
+    load();
+}
+
+void db_playauto_cb(WMWidget *self, void *data) {
+    if (cur_cdmode == WM_CDM_EJECTED) return;
+    if (cd->autoplay) {
+	cd->autoplay = 0;
+    } else {
+	cd->autoplay = 1;
+    }
+    save();
+    load();
+}
+
+void db_volume_cb(WMWidget *self, void *data) {
+    if (cur_cdmode != WM_CDM_EJECTED) {
+	cd->volume = WMGetSliderValue(self);
+	save();
+	load();
+	volume = cd->volume;
+	cd_volume(volume, 10, max_volume);
+	redraw = TRUE;
+	WMSetSliderMinValue(b_volume, min_volume);
+	WMSetSliderMaxValue(b_volume, max_volume);
+	WMSetSliderValue(b_volume, volume);
+    }
+}
+
+void create_db_window(WMScreen *scr) {
+    char txt[256];
+    WMWidget *fr, *fr2;
+    WMWidget *lb1, *lb2, *lb3;
+
+    dbwin = WMCreateWindow(scr, "AScd-db");
+    WMSetWindowCloseAction(dbwin, close_dbwin, NULL);
+    WMSetWindowTitle(dbwin, "AScd database edition");
+    WMResizeWidget(dbwin, 350, 220);
+
+
+    fr = WMCreateFrame(dbwin);
+    WMMoveWidget(fr, 10, 05);
+    WMResizeWidget(fr, 330, 105);
+    WMSetFrameTitle(fr, "CD info:");
+
+    lb1 = WMCreateLabel(dbwin);
+    WMResizeWidget(lb1, 40, 20);
+    WMMoveWidget(lb1, 16, 20);
+    WMSetLabelText(lb1, "Artist:");
+
+    db_artist = WMCreateTextField(dbwin);
+    WMMoveWidget(db_artist, 70, 20);
+    WMResizeWidget(db_artist, 255, 20);
+
+    lb2 = WMCreateLabel(dbwin);
+    WMResizeWidget(lb2, 40, 20);
+    WMMoveWidget(lb2, 16, 40);
+    WMSetLabelText(lb2, "Title:");
+
+    db_title = WMCreateTextField(dbwin);
+    WMMoveWidget(db_title, 70, 40);
+    WMResizeWidget(db_title, 255, 20);
+
+    db_playauto = WMCreateButton(dbwin, WBTSwitch);
+    WMMoveWidget(db_playauto, 70, 65);
+    WMResizeWidget(db_playauto, 255, 20);
+    WMSetButtonText(db_playauto, "play this CD automatically (WorkMan)");
+    WMSetButtonAction(db_playauto, db_playauto_cb, NULL);
+
+    lb3 = WMCreateLabel(dbwin);
+    WMResizeWidget(lb3, 54, 20);
+    WMMoveWidget(lb3, 16, 85);
+    WMSetLabelText(lb3, "Volume:");
+
+    db_volume = WMCreateSlider(dbwin);
+    WMResizeWidget(db_volume, 255, 15);
+    WMMoveWidget(db_volume, 70, 88);
+    WMSetSliderKnobThickness(db_volume, 22);
+    WMSetSliderAction(db_volume, db_volume_cb, NULL);
+
+    /* --- */
+
+    fr2 = WMCreateFrame(dbwin);
+    WMMoveWidget(fr2, 10, 115);
+    WMResizeWidget(fr2, 330, 65);
+    WMSetFrameTitle(fr2, "Track info:");
+
+    db_tlabel = WMCreateLabel(dbwin);
+    WMResizeWidget(db_tlabel, 54, 20);
+    WMMoveWidget(db_tlabel, 16, 130);
+    sprintf(txt, "Track %d:", db_curtrack);
+    WMSetLabelText(db_tlabel, txt);
+
+    db_track = WMCreateTextField(dbwin);
+    WMMoveWidget(db_track, 70, 130);
+    WMResizeWidget(db_track, 255, 20);
+
+    db_avoid = WMCreateButton(dbwin, WBTSwitch);
+    WMMoveWidget(db_avoid, 70, 152);
+    WMResizeWidget(db_avoid, 235, 20);
+    WMSetButtonText(db_avoid, "don't play this track");
+    WMSetButtonAction(db_avoid, db_avoid_cb, NULL);
+
+    /* --- */
+
+    db_prev = WMCreateButton(dbwin, 0);
+    WMResizeWidget(db_prev, 110, 25);
+    WMMoveWidget(db_prev, 10, 190);
+    WMSetButtonText(db_prev, "Previous Track");
+    WMSetButtonAction(db_prev, db_prev_cb, NULL);
+
+    db_next = WMCreateButton(dbwin, 0);
+    WMResizeWidget(db_next, 110, 25);
+    WMMoveWidget(db_next, 125, 190);
+    WMSetButtonText(db_next, "Next Track");
+    WMSetButtonAction(db_next, db_next_cb, NULL);
+
+    db_close = WMCreateButton(dbwin, 0);
+    WMResizeWidget(db_close, 55, 25);
+    WMMoveWidget(db_close, 285, 190);
+    WMSetButtonText(db_close, "Close");
+    WMSetButtonAction(db_close, close_dbwin, NULL);
+
+    WMRealizeWidget(dbwin);
+}
+
+void db_window(WMScreen *scr) {
+    char txt[256];
+
+    if (! db_en_vue) {
+	if (cur_cdmode == WM_CDM_EJECTED) return;
+
+	/* 0.13 fix */
+	if (db_curtrack < 1) db_curtrack = 1;
+
+	load();
+	if (cd->artist != NULL) strcpy(txt, cd->artist);
+	else strcpy(txt, "");
+	WMSetTextFieldText(db_artist, txt);
+
+	if (cd->cdname != NULL) strcpy(txt, cd->cdname);
+	else strcpy(txt, "");
+	WMSetTextFieldText(db_title, txt);
+
+	if (cd->autoplay) {
+	    WMSetButtonSelected(db_playauto, TRUE);
+	} else {
+	    WMSetButtonSelected(db_playauto, TRUE);
+	}
+
+	if (cd->trk[db_curtrack - 1].songname != NULL) {
+	    sprintf(txt,  cd->trk[db_curtrack - 1].songname);
+	} else {
+	    strcpy(txt, "");
+	}
+	WMSetTextFieldText(db_track, txt);
+
+	if (cd->trk[db_curtrack - 1].avoid) {
+	    WMSetButtonSelected(db_avoid, TRUE);
+	} else {
+	    WMSetButtonSelected(db_avoid, FALSE);
+	}
+
+	WMSetSliderMinValue(db_volume, min_volume);
+	WMSetSliderMaxValue(db_volume, max_volume);
+	if (cd->volume > 0) WMSetSliderValue(db_volume, cd->volume);
+	else WMSetSliderValue(db_volume, max_volume);
+
+	WMRealizeWidget(dbwin);
+	WMMapSubwidgets(dbwin);
+	WMMapWidget(dbwin);
+	db_en_vue = TRUE;
+    } else {
+	WMCloseWindow(dbwin);
+	db_en_vue = FALSE;
+    }
+}
+
+void db_cb(WMWidget *self, void *data) {
+    char txt[127];
+    sprintf(txt, "Tk %d:", db_curtrack);
+    WMSetLabelText(db_tlabel, txt);
+    /*WMRealizeWidget(dbwin);*/
+    db_window(scr);
+}
+
+void about_cb(WMWidget *self, void *data) {
+    about_window(scr);
+}
+
+void create_big_window(WMScreen *scr) {
+    WMWidget *fr, *fr3, *fr4, *fr5;
+    WMWidget *lb1, *lb2, *lb3, *lb4, *lb6;
+
+    win = WMCreateWindow(scr, "AScd");
+    WMSetWindowCloseAction(win, close_win, NULL);
+    WMSetWindowTitle(win, "AScd");
+    WMResizeWidget(win, 320, 370);
+
+    b_title = WMCreateButton(win, 0);
+    WMResizeWidget(b_title, 300, 40);
+    WMMoveWidget(b_title, 10, 5);
+    WMSetButtonText(b_title, "1\n2");
+    WMSetButtonAction(b_title, db_cb, NULL);
+
+    pop = WMCreatePopUpButton(win);
+    WMResizeWidget(pop, 300, 20);
+    WMMoveWidget(pop, 10, 50);
+    WMSetPopUpButtonPullsDown(pop, True);
+    WMSetPopUpButtonText(pop, "Tracks");
+    WMAddPopUpButtonItem(pop, "1");
+    WMSetPopUpButtonAction(pop, track_select, NULL); 
+
+    fr = WMCreateFrame(win);
+    WMMoveWidget(fr, 10, 75);
+    WMResizeWidget(fr, 110, 140);
+    WMSetFrameTitle(fr, "Playback:");
+
+    b_autoplay = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_autoplay, 20, 95);
+    WMResizeWidget(b_autoplay, 90, 20);
+    WMSetButtonText(b_autoplay, "autoplay");
+    WMSetButtonAction(b_autoplay, autoplay_cb, NULL);
+
+    b_autorepeat = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_autorepeat, 20, 115);
+    WMResizeWidget(b_autorepeat, 90, 20);
+    WMSetButtonText(b_autorepeat, "autorepeat");
+    WMSetButtonAction(b_autorepeat, autorepeat_cb, NULL);
+
+    b_ignoreavoid = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_ignoreavoid, 20, 135);
+    WMResizeWidget(b_ignoreavoid, 90, 20);
+    WMSetButtonText(b_ignoreavoid, "apply skips");
+    WMSetButtonAction(b_ignoreavoid, ignoreavoid_cb, NULL);
+
+    lb6 = WMCreateLabel(win);
+    WMResizeWidget(lb6, 80, 20);
+    WMMoveWidget(lb6, 15, 165);
+    WMSetLabelText(lb6, "CD Device:");
+
+    b_device = WMCreateTextField(win);
+    WMResizeWidget(b_device, 85, 20);
+    WMMoveWidget(b_device, 20, 185);
+
+
+    fr3 = WMCreateFrame(win);
+    WMMoveWidget(fr3, 130, 75);
+    WMResizeWidget(fr3, 180, 140);
+    WMSetFrameTitle(fr3, "Display:");
+
+    b_scroll = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_scroll, 140, 95);
+    WMResizeWidget(b_scroll, 130, 20);
+    WMSetButtonText(b_scroll, "scroll song names");
+    WMSetButtonAction(b_scroll, scroll_cb, NULL);
+
+    b_artist = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_artist, 140, 115);
+    WMResizeWidget(b_artist, 130, 20);
+    WMSetButtonText(b_artist, "add artist name");
+    WMSetButtonAction(b_artist, artist_cb, NULL);
+
+    b_upper = WMCreateButton(win, WBTSwitch);
+    WMMoveWidget(b_upper, 140, 135);
+    WMResizeWidget(b_upper, 130, 20);
+    WMSetButtonText(b_upper, "force uppercase");
+    WMSetButtonAction(b_upper, upper_cb, NULL);
+
+    lb4 = WMCreateLabel(win);
+    WMResizeWidget(lb4, 80, 20);
+    WMMoveWidget(lb4, 135, 165);
+    WMSetLabelText(lb4, "Theme:");
+
+    pop2 = WMCreatePopUpButton(win);
+    WMResizeWidget(pop2, 160, 20);
+    WMMoveWidget(pop2, 140, 185);
+    WMSetPopUpButtonText(pop2, theme);
+    WMAddPopUpButtonItem(pop2, "1");
+    WMSetPopUpButtonAction(pop2, theme_pick, NULL); 
+
+    b_help = WMCreateButton(win, 0);
+    WMResizeWidget(b_help, 80, 18);
+    WMMoveWidget(b_help, 220, 165);
+    WMSetButtonText(b_help, "Quick Ref.");
+    WMSetButtonAction(b_help, help_cb, NULL);
+
+    /* the volume */
+
+    fr4 = WMCreateFrame(win);
+    WMMoveWidget(fr4, 10, 220);
+    WMResizeWidget(fr4, 300, 80);
+    WMSetFrameTitle(fr4, "Volume & Misc:");
+
+    b_minvol = WMCreateTextField(win);
+    WMResizeWidget(b_minvol, 30, 20);
+    WMMoveWidget(b_minvol, 20, 240);
+
+    b_volume = WMCreateSlider(win);
+    WMResizeWidget(b_volume, 220, 20);
+    WMMoveWidget(b_volume, 50, 240);
+    WMSetSliderKnobThickness(b_volume, 22);
+    WMSetSliderAction(b_volume, volume_cb, NULL);
+
+    b_maxvol = WMCreateTextField(win);
+    WMResizeWidget(b_maxvol, 30, 20);
+    WMMoveWidget(b_maxvol, 270, 240);
+
+    lb1 = WMCreateLabel(win);
+    WMResizeWidget(lb1, 40, 20);
+    WMMoveWidget(lb1, 15, 270);
+    WMSetLabelText(lb1, "Muted");
+
+    b_mutedvol = WMCreateTextField(win);
+    WMResizeWidget(b_mutedvol, 30, 20);
+    WMMoveWidget(b_mutedvol, 65, 270);
+
+    lb2 = WMCreateLabel(win);
+    WMResizeWidget(lb2, 70, 20);
+    WMMoveWidget(lb2, 105, 270);
+    WMSetLabelText(lb2, "Cue Time");
+
+    b_cuetime = WMCreateTextField(win);
+    WMResizeWidget(b_cuetime, 25, 20);
+    WMMoveWidget(b_cuetime, 180, 270);
+
+    lb3 = WMCreateLabel(win);
+    WMResizeWidget(lb3, 33, 20);
+    WMMoveWidget(lb3, 225, 270);
+    WMSetLabelText(lb3, "Fade");
+
+    b_fadestep = WMCreateTextField(win);
+    WMResizeWidget(b_fadestep, 25, 20);
+    WMMoveWidget(b_fadestep, 275, 270);
+
+    /* the counter modes */
+
+    fr5 = WMCreateFrame(win);
+    WMMoveWidget(fr5, 10, 305);
+    WMResizeWidget(fr5, 190, 60);
+    WMSetFrameTitle(fr5, "Counter Mode:");
+
+    b_mode1 = WMCreateButton(win, WBTRadio);
+    WMMoveWidget(b_mode1, 20, 320);
+    WMResizeWidget(b_mode1, 55, 20);
+    WMSetButtonText(b_mode1, "Track");
+    WMSetButtonAction(b_mode1, mode1_cb, NULL);
+
+    b_mode2 = WMCreateButton(win, WBTRadio);
+    WMMoveWidget(b_mode2, 20, 340);
+    WMResizeWidget(b_mode2, 55, 20);
+    WMSetButtonText(b_mode2, "CD");
+    WMSetButtonAction(b_mode2, mode2_cb, NULL);
+
+    b_mode3 = WMCreateButton(win, WBTRadio);
+    WMMoveWidget(b_mode3, 90, 320);
+    WMResizeWidget(b_mode3, 85, 20);
+    WMSetButtonText(b_mode3, "Elapsed");
+    WMSetButtonAction(b_mode3, mode3_cb, NULL);
+
+    b_mode4 = WMCreateButton(win, WBTRadio);
+    WMMoveWidget(b_mode4, 90, 340);
+    WMResizeWidget(b_mode4, 85, 20);
+    WMSetButtonText(b_mode4, "Remaining");
+    WMSetButtonAction(b_mode4, mode4_cb, NULL);
+
+    /* the global buttons */
+
+    b_save = WMCreateButton(win, 0);
+    WMResizeWidget(b_save, 50, 25);
+    WMMoveWidget(b_save, 205, 340);
+    WMSetButtonText(b_save, "Save");
+    WMSetButtonAction(b_save, save_cb, NULL);
+
+    b_about = WMCreateButton(win, 0);
+    WMResizeWidget(b_about, 50, 25);
+    WMMoveWidget(b_about, 205, 310);
+    WMSetButtonText(b_about, "Info");
+    WMSetButtonAction(b_about, about_cb, NULL);
+
+    b_save = WMCreateButton(win, 0);
+    WMResizeWidget(b_save, 50, 25);
+    WMMoveWidget(b_save, 260, 310);
+    WMSetButtonText(b_save, "Quit");
+    WMSetButtonAction(b_save, quit_ascd, NULL);
+
+    b_close = WMCreateButton(win, 0);
+    WMResizeWidget(b_close, 50, 25);
+    WMMoveWidget(b_close, 260, 340);
+    WMSetButtonText(b_close, "Close");
+    WMSetButtonAction(b_close, close_win, NULL);
+
+    /* finished, draw everything */
+
+    WMRealizeWidget(win);
+
+    /*
+    WMMapSubwidgets(win);
+    WMMapWidget(win);
+    */
+}
+
+void update_track() {
+    int i;
+    char txt[127];
+    char txt2[127];
+
+    if (en_vue) {
+	if (cur_cdmode != WM_CDM_EJECTED) {
+
+	    if (cd->artist != NULL) {
+		strcpy(txt, cd->artist);
+		if (strcmp(txt, "") == 0) strcpy(txt, "unknown artist");
+	    } else {
+		strcpy(txt, "unknown artist");
+	    }
+	    
+	    strcat(txt, "\n");
+
+	    if (cd->cdname != NULL) {
+		strcpy(txt2, cd->cdname);
+		if (strcmp(txt2, "") == 0) strcpy(txt2, "unknown album");
+	    } else {
+		strcpy(txt2, "unknown album");
+	    }
+
+	    strcat(txt, txt2);
+	    WMSetButtonText(b_title, txt);
+
+	    for (i = WMGetPopUpButtonNumberOfItems(pop); i >= 0; i --) {
+		WMRemovePopUpButtonItem(pop, i);
+	    }
+
+	    for (i = 0; i < cur_ntracks; i++) {
+		sprintf(txt, "%2d ", i + 1);
+		if (cd->trk[i].songname != NULL) strcat(txt,  cd->trk[i].songname);
+		WMAddPopUpButtonItem(pop, txt);
+	    }
+
+	    pistes = cur_ntracks;
+	    
+	    if (cur_track >= 1) {
+		if (cd->trk[cur_track - 1].songname != NULL) {
+		    sprintf(txt, "%s", cd->trk[cur_track - 1].songname);
+		    if (strcmp(txt, "") == 0) sprintf(txt, "Track %d", cur_track);
+		    WMSetPopUpButtonText(pop, txt);
+		} else {
+		    sprintf(txt, "Track %d", cur_track);
+		    WMSetPopUpButtonText(pop, txt);
+		}
+	    } else {
+		WMSetPopUpButtonText(pop, "Track 1");
+	    }
+
+	} else {
+	    WMSetButtonText(b_title, "No CD in drive\nor the tray is not loaded");
+	}
+    }
+}
+
+void big_window(WMScreen *scr) {
+    int i;
+    char txt[256];
+    DIR *dir_fd;
+    struct dirent *dir_pt;
+
+    if (! en_vue) {
+
+	/*if (cur_cdmode == WM_CDM_EJECTED) return;*/
+
+	en_vue = TRUE;
+
+	sprintf(txt, "%d", cue_time);
+	WMSetTextFieldText(b_cuetime, txt);
+	sprintf(txt, "%d", fade_step);
+	WMSetTextFieldText(b_fadestep, txt);
+	sprintf(txt, "%d", min_volume);
+	WMSetTextFieldText(b_minvol, txt);
+	sprintf(txt, "%d", max_volume);
+	WMSetTextFieldText(b_maxvol, txt);
+	sprintf(txt, "%d", muted_volume);
+	WMSetTextFieldText(b_mutedvol, txt);
+
+	WMSetTextFieldText(b_device, cd_device);
+	
+	WMSetSliderMinValue(b_volume, min_volume);
+	WMSetSliderMaxValue(b_volume, max_volume);
+	WMSetSliderValue(b_volume, volume);
+
+	/* remove everything in the two popups */
+
+	for (i = WMGetPopUpButtonNumberOfItems(pop); i >= 0; i --) {
+	    WMRemovePopUpButtonItem(pop, i);
+	}
+
+	for (i = WMGetPopUpButtonNumberOfItems(pop2); i >= 0; i --) {
+	    WMRemovePopUpButtonItem(pop2, i);
+	}
+
+	/* update the database stuff. AScd 0.13: I deleted all this 
+	   stuff from this function as update_track() do the same! */
+
+	update_track();
+
+	/* make the theme select menu */
+
+	sprintf(txt, "%s/Themes", THDIR);
+	if ((dir_fd = opendir(txt)) != NULL) {
+	    while((dir_pt = readdir(dir_fd)) != NULL) {
+		if (dir_pt->d_name[0] != '.') {
+		    WMAddPopUpButtonItem(pop2, dir_pt->d_name);
+		}
+	    }
+	    closedir(dir_fd);
+	}
+	WMSetPopUpButtonText(pop2, theme);
+
+	/* several options toggles */
+
+	if (autoplay) {
+	    WMSetButtonSelected(b_autoplay, TRUE);
+	} else {
+	    WMSetButtonSelected(b_autoplay, FALSE);
+	}
+
+	if (autorepeat) {
+	    WMSetButtonSelected(b_autorepeat, TRUE);
+	} else {
+	    WMSetButtonSelected(b_autorepeat, FALSE);
+	}
+
+	if (show_db) {
+	    WMSetButtonSelected(b_scroll, TRUE);
+	} else {
+	    WMSetButtonSelected(b_scroll, FALSE);
+	}
+
+	if (show_artist) {
+	    WMSetButtonSelected(b_artist, TRUE);
+	} else {
+	    WMSetButtonSelected(b_artist, FALSE);
+	}
+
+	if (ignore_avoid) {
+	    WMSetButtonSelected(b_ignoreavoid, FALSE);
+	} else {
+	    WMSetButtonSelected(b_ignoreavoid, TRUE);
+	}
+
+	if (force_upper) {
+	    WMSetButtonSelected(b_upper, TRUE);
+	} else {
+	    WMSetButtonSelected(b_upper, FALSE);
+	}
+
+	/* finally, the counter mode radio buttons */
+
+	switch(time_mode) {
+	case 0:
+	    WMSetButtonSelected((b_mode1), TRUE);
+	    WMSetButtonSelected((b_mode2), FALSE);
+	    WMSetButtonSelected((b_mode3), TRUE);
+	    WMSetButtonSelected((b_mode4), FALSE);
+	    break;
+	case 1:
+	    WMSetButtonSelected((b_mode1), TRUE);
+	    WMSetButtonSelected((b_mode2), FALSE);
+	    WMSetButtonSelected((b_mode4), TRUE);
+	    WMSetButtonSelected((b_mode3), FALSE);
+	    break;
+	case 2:
+	    WMSetButtonSelected((b_mode2), TRUE);
+	    WMSetButtonSelected((b_mode1), FALSE);
+	    WMSetButtonSelected((b_mode3), TRUE);
+	    WMSetButtonSelected((b_mode4), FALSE);
+	    break;
+	default:
+	    WMSetButtonSelected((b_mode2), TRUE);
+	    WMSetButtonSelected((b_mode1), FALSE);
+	    WMSetButtonSelected((b_mode4), TRUE);
+	    WMSetButtonSelected((b_mode3), FALSE);
+	    break;
+	}
+
+	WMRealizeWidget(win);
+	WMMapSubwidgets(win);
+	WMMapWidget(win);
+    } else {
+	WMCloseWindow(win);
+	muted_volume = atoi(WMGetTextFieldText(b_mutedvol));
+	min_volume = atoi(WMGetTextFieldText(b_minvol));
+	max_volume = atoi(WMGetTextFieldText(b_maxvol));
+	cue_time = atoi(WMGetTextFieldText(b_cuetime));
+	fade_step = atoi(WMGetTextFieldText(b_fadestep));
+	en_vue = FALSE;
+    }
+}
diff --git a/ascd/ascd/workman/include b/ascd/ascd/workman/include
new file mode 120000
index 0000000..4251ee6
--- /dev/null
+++ b/ascd/ascd/workman/include
@@ -0,0 +1 @@
+../libworkman/include
\ No newline at end of file
diff --git a/ascd/ascd/workman/wm_cdda.h b/ascd/ascd/workman/wm_cdda.h
new file mode 100644
index 0000000..831b19e
--- /dev/null
+++ b/ascd/ascd/workman/wm_cdda.h
@@ -0,0 +1,117 @@
+#ifndef WM_CDDA_H
+#define WM_CDDA_H
+/*
+ * $Id: wm_cdda.h,v 1.2 1999/02/14 09:50:46 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * Information about a particular block of CDDA data.
+ */
+struct cdda_block {
+	unsigned char	status;		/* see below */
+	unsigned char	track;
+	unsigned char	index;
+	unsigned char	minute;
+	unsigned char	second;
+	unsigned char	frame;
+
+	/* Average volume levels, for level meters */
+	unsigned char	lev_chan0;
+	unsigned char	lev_chan1;
+
+	/* Current volume setting (0-255) */
+	unsigned char	volume;
+
+	/* Current balance setting (0-255, 128 = balanced) */
+	unsigned char	balance;
+};
+
+/*
+ * cdda_block status codes.
+ */
+#define WMCDDA_ERROR	0	/* Couldn't read CDDA from disc */
+#define WMCDDA_OK	1	/* Read this block successfully (raw data) */
+#define WMCDDA_PLAYED	2	/* Just played the block in question */
+#define WMCDDA_STOPPED	3	/* Block data invalid; we've just stopped */
+#define WMCDDA_ACK	4	/* Acknowledgement of command from parent */
+#define WMCDDA_DONE	5	/* Chunk of data is done playing */
+#define WMCDDA_EJECTED	6	/* Disc ejected or offline */
+
+/*
+ * Enable or disable CDDA building depending on platform capabilities, and
+ * determine endianness based on architecture.  (Gross!)
+ *
+ * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be
+ * renamed. At least Linux does have bytesex.h and endian.h for easy
+ * byte-order examination.
+ */
+
+#ifdef sun
+# ifdef SYSV
+#  include <sys/types.h>
+#  include <sys/cdio.h>
+#  ifndef CDROMCDDA
+#   undef BUILD_CDDA
+#  endif
+#  ifdef i386
+#   define WM_LITTLE_ENDIAN 1
+#   define WM_BIG_ENDIAN 0
+#  else
+#   define WM_BIG_ENDIAN 1
+#   define WM_LITTLE_ENDIAN 0
+#  endif
+# else
+#  undef BUILD_CDDA
+# endif
+#endif
+
+/* Linux only allows definition of endianness, because there's no
+ * standard interface for CDROM CDDA functions that aren't available
+ * if there is no support.
+ */
+#ifdef linux
+# include <bytesex.h>
+# include <endian.h>
+/*
+ * XXX could this be a problem? The results are only 0 and 1 because
+ * of the ! operator. How about other linux compilers than gcc ?
+ */
+# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN)
+# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN)
+#endif
+
+/*
+ * The following code shouldn't take effect now. 
+ * In 1998, the WorkMan platforms don't support __PDP_ENDIAN
+ * architectures.
+ *
+ */
+
+#if !WM_LITTLE_ENDIAN
+#  if !WM_BIG_ENDIAN
+#    error yet unsupported architecture
+	foo bar this is to stop the compiler. 
+#  endif
+#endif
+#endif /* WM_CDDA_H */
diff --git a/ascd/ascd/workman/wm_cddb.h b/ascd/ascd/workman/wm_cddb.h
new file mode 100644
index 0000000..bd5b4f9
--- /dev/null
+++ b/ascd/ascd/workman/wm_cddb.h
@@ -0,0 +1,42 @@
+#ifndef WM_CDDB_H
+#define WM_CDDB_H
+/*
+ * $Id: wm_cddb.h,v 1.2 1999/02/14 09:50:46 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+extern struct wm_cddb cddb;
+extern int cur_cddb_protocol;
+extern char *cur_cddb_server, *cur_cddb_mail_adress,
+	*cur_cddb_path_to_cgi, *cur_cddb_proxy_server;
+
+unsigned int cddb_discid();
+void cddb_struct2cur();
+void cddb_cur2struct();
+void cddb_select();
+void connect_cddb();
+void update_cddbserver();
+void cddb_request();
+
+#endif /* WM_CDDB_H */
diff --git a/ascd/ascd/workman/wm_cdinfo.h b/ascd/ascd/workman/wm_cdinfo.h
new file mode 100644
index 0000000..f783465
--- /dev/null
+++ b/ascd/ascd/workman/wm_cdinfo.h
@@ -0,0 +1,78 @@
+#ifndef WM_CDINFO_H
+#define WM_CDINFO_H
+/*
+ * $Id: wm_cdinfo.h,v 1.5 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes from cdinfo.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+extern char *cur_trackname;		/* Take a guess */
+extern int cur_index;			/* Current index mark */
+extern int cur_frame;			/* Current frame number */
+extern struct wm_play *playlist;		/* = NULL */
+extern struct wm_cdinfo thiscd;
+extern struct wm_cdinfo *cd;
+extern int cur_track;			/* Current track number, starting at 1 */
+extern char *cur_artist;		/* Name of current CD's artist */
+extern char cur_avoid;			/* Avoid flag */
+extern char cur_contd;			/* Continued flag */
+extern char *cur_cdname;		/* Album name */
+extern int cur_nsections;		/* Number of sections currently defined */
+extern int exit_on_eject;
+extern int cur_track;
+extern int cur_pos_abs;
+extern int cur_pos_rel;
+extern int cur_tracklen;
+extern int cur_cdlen;
+extern enum wm_cd_modes cur_cdmode;
+extern int cur_ntracks;
+extern int cur_lasttrack;
+extern int cur_firsttrack;
+extern int cur_listno;
+extern int cur_stopmode;
+
+void	wipe_cdinfo( void );
+void	play_next_entry( int forward );
+void	make_playlist( int playmode, int starttrack );
+int	get_autoplay( void );
+int	get_playmode( void );
+void	pl_find_track( int track );
+void	play_prev_track( int forward );
+void	play_next_track( int forward );
+int	tracklen( int num );
+int	get_default_volume( int track );
+int	split_trackinfo( int pos );
+int	remove_trackinfo( int num );
+void	freeup( char **x );
+int	get_runtime( void );
+char   *trackname( int num );
+void	stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode );
+void	stash_trkinfo( int track, char *songname, int contd, int avoid );
+int	get_avoid( int num );
+int	get_contd( int num );
+void	default_volume( int track, int vol );
+char   *listentry( int num );
+
+#endif /* WM_CDINFO_H */
diff --git a/ascd/ascd/workman/wm_cdrom.h b/ascd/ascd/workman/wm_cdrom.h
new file mode 100644
index 0000000..7d0dca4
--- /dev/null
+++ b/ascd/ascd/workman/wm_cdrom.h
@@ -0,0 +1,64 @@
+#ifndef WM_CDROM_H
+#define WM_CDROM_H
+/*
+ * $Id: wm_cdrom.h,v 1.6 1999/05/05 16:09:55 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes from cdrom.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#define WM_CDS_NO_DISC		0
+#define WM_CDS_DISC_READY	1
+#define WM_CDS_JUST_INSERTED	2
+
+#define WM_STR_GENVENDOR	"Generic"
+#define WM_STR_GENMODEL		"drive"
+#define WM_STR_GENREV		"type"
+
+extern int wm_cd_cur_balance;
+
+
+char *	wm_drive_vendor( void );
+char *	wm_drive_model( void );
+char *	wm_drive_revision( void );
+void 	wm_drive_settype( char *vendor, char *model, char *revision );
+
+int	wm_cd_status( void );
+
+void	wm_cd_play( int start, int pos, int end );
+void	wm_cd_play_chunk( int start, int end, int realstart );
+void	wm_cd_play_from_pos( int pos );
+void	wm_cd_pause( void );
+void	wm_cd_stop( void );
+int	wm_cd_eject( void );
+int     wm_cd_closetray( void );
+int	wm_cd_read_initial_volume( int max );
+
+/*
+ * Following are the missing to rename.
+ */
+int	find_trkind( int track, int index, int start );
+void	cd_volume( int vol, int bal, int max );
+
+#endif /* WM_CDROM_H */
diff --git a/ascd/ascd/workman/wm_config.h b/ascd/ascd/workman/wm_config.h
new file mode 100644
index 0000000..c04a0e5
--- /dev/null
+++ b/ascd/ascd/workman/wm_config.h
@@ -0,0 +1,357 @@
+#ifndef WM_CONFIG_H
+#define WM_CONFIG_H
+/*
+ * $Id: wm_config.h,v 1.5 1999/05/05 16:34:22 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ **********************************************************************
+ *
+ * This file consists of several parts. First, there's a generic,
+ * platform independent part. Set needed options there.
+ * The following parts are platform dependent. You may search for the
+ * names listed below and then set your OS specific options there.
+ * Don't be surprised, if there are no options for your OS. They aren't
+ * needed in any case.
+ * 
+ * The default values should produce a functional WorkMan on every
+ * platform.
+ *
+ *********************
+ * Current platforms:
+ *********************
+ * BSD386
+ * FreeBSD
+ * HP-UX
+ * Irix (SGI)
+ * Linux
+ * News  (Sony NewsOS)
+ * OpenBSD
+ * OSF1
+ * Sun (SunOS/Solaris, Sparc or x86)
+ * SVR4
+ * Ultrix
+ * AIX
+ *
+ * The above order corresponds with the order of the platform specific
+ * options below.
+ */
+
+/******************************************************************
+ * generic options
+ ******************************************************************
+ **   **  ***  ****    **  ****   **    *    **   **    **   *   **
+ *  *  *  ***  ****  *  *  ***  *  **  **  ***  *  *  *  *  * *  **
+ *     *  ***  ****    **  ***     **  **   **  *  *    **  * *  **
+ *  *  *  ***  ****  ****  ***  *  **  **  ***  *  *  *  *  ***  **
+ *  *  *    *    **  ****    *  *  **  **  ****   **  *  *  ***  **
+ ******************************************************************/
+
+/*
+ * This option is obvious. But please do not forget the original
+ * WorkMan version string if you need support.
+ */
+
+#define WORKMAN_NAME "LibWorkMan"
+#define WORKMAN_VERSION "1.4.0"
+ 
+/*
+ * If your CD-ROM drive closes its tray if the device is opened, then
+ * the next define can make WorkMans "Eject" button an "open/close"
+ * button. If it disturbs you, just comment it out.
+ *
+ * ### this is preliminary. It may have no effect for you ###
+ */
+#define CAN_CLOSE 1
+
+/*
+ * Define the following if you want the balance slider to
+ * decrease one channel's volume while increasing the other's
+ */
+/* #define SYMETRIC_BALANCE 1 */
+
+
+/*
+ * Define this if you want CDDA support. Supported systems are:
+ *
+ * - Solaris (2.4 or newer)
+ * --> Linux is on the way. Don't define it now. It would lead to errors only.
+ */
+/*#define BUILD_CDDA 1*/
+
+/*
+ *
+ * This is for experimental cddb support.
+ * This activates the UI component.
+ */
+
+#define CDDB_IN_UI 1
+
+
+/******************************************************************
+ * BSD386
+ ******************************************************************
+ ***     ****    ***    *******  **    ****    ****    ************
+ ***  **  **  ******  *  *****  ******  **  **  **  ***************
+ ***    ******  ****  **  ***  *****  *****    ***     ************
+ ***  **  ******  **  *  ***  ********  **  **  **  **  ***********
+ ***     ****    ***    ***  ******    ****    ****    ************
+ ******************************************************************/
+#ifdef __bsdi__
+
+/*
+ * This lets you use the SoundBlaster Mixer on BSD/386
+ */
+#define SOUNDBLASTER 1
+
+#endif /* __bsdi__ (BSD/386) */
+
+/******************************************************************
+ * FreeBSD
+ ******************************************************************
+ ***      **     ***      **      **     ****    ***    ***********
+ ***  ******  **  **  ******  ******  **  **  ******  *  **********
+ ***    ****     ***    ****    ****    ******  ****  **  *********
+ ***  ******  **  **  ******  ******  **  ******  **  *  **********
+ ***  ******  **  **      **      **     ****    ***    ***********
+ ******************************************************************/
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+
+#define DEFAULT_CD_DEVICE	"/dev/rmatcd0c"
+
+#endif /* freebsd */
+
+/******************************************************************
+ * HP-UX
+ ******************************************************************
+ ***  **  **     *********  **  **  **  ***************************
+ ***  **  **  **  ********  **  ***    ****************************
+ ***      **     ***    **  **  ****  *****************************
+ ***  **  **  ************  **  ***    ****************************
+ ***  **  **  *************    ***  **  ***************************
+ ******************************************************************/
+#if defined(hpux) || defined (__hpux)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rscsi"
+
+#endif /* hpux */
+
+/******************************************************************
+ * Irix
+ ******************************************************************
+ ***      **     ***      **  **  *********************************
+ *****  ****  **  ****  *****    **********************************
+ *****  ****     *****  ******  ***********************************
+ *****  ****  **  ****  *****    **********************************
+ ***      **  **  **      **  **  *********************************
+ ******************************************************************/
+#if defined(sgi)
+
+#define DEFAULT_CD_DEVICE	"/dev/scsi/sc0d6l0"
+
+#endif /* sgi IRIX */
+
+/******************************************************************
+ * Linux
+ ******************************************************************
+ ***  ******      **   ***  **  **  **  **  ***********************
+ ***  ********  ****    **  **  **  ***    ************************
+ ***  ********  ****  *  *  **  **  ****  *************************
+ ***  ********  ****  **    **  **  ***    ************************
+ ***      **      **  ***   ***    ***  **  ***********************
+ ******************************************************************/
+#ifdef linux
+
+/*
+ * Uncomment the following line to have WorkMan send SCSI commands
+ *  directly to the CD-ROM drive.  If you have a SCSI drive you 
+ * probably want this, but it will cause WorkMan to not work on IDE
+ * drives.
+ */
+#define LINUX_SCSI_PASSTHROUGH 1
+
+/*
+ * Which device should be opened by WorkMan at default?
+ */
+#define DEFAULT_CD_DEVICE	"/dev/sbpcd"
+
+/*
+ * Uncomment the following if you use the sbpcd or mcdx device driver.
+ * It shouldn't hurt if you use it on other devices. It'll be nice to
+ * hear from non-sbpcd (or mcdx) users if this is right.
+ */
+/*#define SBPCD_HACK 1*/
+
+/*
+ * Linux Soundcard support
+ */
+#define OSS_SUPPORT 1
+
+/*
+ * This has nothing to do with the above.
+ */
+
+/* #define CURVED_VOLUME */
+ 
+/*
+ * Uncomment the following if you want to try out a better responding
+ * WorkMan, especially with IDE drives. This may work with non-IDE
+ * drives as well. But it may be possible, that it doesn't work at all.
+ * If your drive/driver combination cannot handle the faster access,
+ * the driver will usually hang and you have to reboot your machine.
+ */
+/* #define FAST_IDE 1 */
+
+/*
+ * There are two alternative ways of checking a device containing a
+ * mounted filesystem. Define BSD_MOUNTTEST for the test using
+ * getmntent(). Undefine it for using the SVR4 ustat().
+ * I built in the choice, because it's not clear which method should
+ * be used in Linux. The ustat manpage tells us since 1995, that
+ * fstat() should be used, but I'm too dumb to do so.
+ */
+
+#define BSD_MOUNTTEST 
+
+#endif /* linux */
+
+/******************************************************************
+ * Sony NewsOS
+ ******************************************************************
+ ***   ***  **      **  *****  ***    *****************************
+ ***    **  **  ******  *****  **  ********************************
+ ***  *  *  **    ****  ** **  ****  ******************************
+ ***  **    **  ******  *   *  ******  ****************************
+ ***  ***   **      **    *    ***    *****************************
+ ******************************************************************/
+#if defined(__sony_news) || defined(sony_news)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rsd/b0i6u0p2\0"
+
+#endif
+
+/******************************************************************
+ * OpenBSD
+ ******************************************************************
+ ****    ***     ***      **   ***  **     ****    ***    *********
+ ***  **  **  **  **  ******    **  **  **  **  ******  *  ********
+ ***  **  **     ***    ****  *  *  **    ******  ****  **  *******
+ ***  **  **  ******  ******  **    **  **  ******  **  *  ********
+ ****    ***  ******      **  ***   **     ****    ***    *********
+ ******************************************************************/
+#ifdef __OpenBSD__
+
+#define	DEFAULT_CD_DEVICE	"/dev/rcdrom"
+
+#endif
+
+/******************************************************************
+ * OSF1
+ ******************************************************************
+ ****    ****    ***      ***  ***  *******************************
+ ***  **  **  ******  ******  **    *******************************
+ ***  **  ****  ****    ***  *****  *******************************
+ ***  **  ******  **  ****  ******  *******************************
+ ****    ****    ***  ***  *****      *****************************
+ ******************************************************************/
+#if defined(__osf__)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * SunOS/Solaris
+ ******************************************************************
+ ****    ***  **  **   ***  ***************************************
+ ***  ******  **  **    **  ***************************************
+ *****  ****  **  **  *  *  ***************************************
+ *******  **  **  **  **    ***************************************
+ ****    ****    ***  ***   ***************************************
+ ******************************************************************/
+#if defined(sun) || defined(__sun__)
+
+/*
+ * Define the following for Solaris 2.x
+ * If you don't want WorkMan to try to activate the SPARCstation 5
+ * internal audio input so you get sound from the workstation, comment
+ * out the CODEC define.
+ */
+
+#define SYSV 1
+#define CODEC 1
+
+/*
+ * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16"
+ * for PC (with SoundBlaster 16) running Solaris x86
+ * (only important if you define CODEC above)
+ */
+/*#define SUN_AUD_DEV "SUNW,CS4231"*/
+#define SUN_AUD_DEV "SUNW,sbpro"
+
+
+#endif
+
+/******************************************************************
+ * SVR4
+ ******************************************************************
+ ****    ***  ****  **     ***  *  ********************************
+ ***  ******  ****  **  **  **  *  ********************************
+ *****  *****  **  ***     ***      *******************************
+ *******  ***  **  ***  **  *****  ********************************
+ ****    *****    ****  **  *****  ********************************
+ ******************************************************************/
+#if defined(SVR4) && !defined(sun) && !defined(__sun__)
+
+#define DEFAULT_CD_DEVICE       "/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * Ultrix
+ ******************************************************************
+ ***  **  **  *****      **     ***      **  **  ******************
+ ***  **  **  *******  ****  **  ****  *****    *******************
+ ***  **  **  *******  ****     *****  ******  ********************
+ ***  **  **  *******  ****  **  ****  *****    *******************
+ ****    ***      ***  ****  **  **      **  **  ******************
+ ******************************************************************/
+#if defined(ultrix) || defined(__ultrix)
+
+#endif
+
+/******************************************************************
+ * IBM AIX
+ ******************************************************************
+ ****    ***      **  **  *****************************************
+ ***  **  ****  *****    ******************************************
+ ***      ****  ******  *******************************************
+ ***  **  ****  *****    ******************************************
+ ***  **  **      **  **  *****************************************
+ ******************************************************************/
+#if defined(AIXV3)
+
+#define DEFAULT_CD_DEVICE	"/dev/cd0"
+
+#endif /* IBM AIX */
+
+/******************************************************************/ 
+#endif /* WM_CONFIG_H */
diff --git a/ascd/ascd/workman/wm_database.h b/ascd/ascd/workman/wm_database.h
new file mode 100644
index 0000000..aaea086
--- /dev/null
+++ b/ascd/ascd/workman/wm_database.h
@@ -0,0 +1,44 @@
+#ifndef WM_DATABASE_H
+#define WM_DATABASE_H
+/*
+ * $Id: wm_database.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes for WorkMan database
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+
+#define WM_DB_SAVE_ERROR	1
+#define WM_DB_SAVE_DISABLED	2
+ 
+int wm_db_get_playnew( void );
+void	split_workmandb( void );
+int	save( void );
+void	load( void );
+void	load_settings( void );
+
+extern int wm_db_save_disabled;
+extern int cur_playnew;
+
+#endif /* WM_DATABASE_H */
diff --git a/ascd/ascd/workman/wm_helpers.h b/ascd/ascd/workman/wm_helpers.h
new file mode 100644
index 0000000..0a3763b
--- /dev/null
+++ b/ascd/ascd/workman/wm_helpers.h
@@ -0,0 +1,105 @@
+#ifndef WM_HELPERS_H
+#define WM_HELPERS_H
+/*
+ * $Id: wm_helpers.h,v 1.6 1999/03/07 08:36:44 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Here to be found: Prototypes. Including variable names to be easier
+ * to read.
+ * This is just one more step to a more modular and understandable code.
+ *
+ */
+
+/*
+ * LibWorkMan message levels. I'm not sure how to call them all and which
+ * use they should fulfill. This is not very urgent now, because there
+ * aren't many messages in LibWorkMan now. 
+ */
+#define WM_MSG_LEVEL_NONE	0
+#define WM_MSG_LEVEL_ERROR	1
+#define WM_MSG_LEVEL_TWO	2
+#define WM_MSG_LEVEL_THREE	3
+#define WM_MSG_LEVEL_FOUR	4
+#define WM_MSG_LEVEL_INFO	5
+#define WM_MSG_LEVEL_SIX	6
+#define WM_MSG_LEVEL_SEVEN	7
+#define WM_MSG_LEVEL_EIGHT	8
+#define WM_MSG_LEVEL_DEBUG	9
+
+/*
+ * Message classes. This is somehow a definition of
+ * the message's source.
+ */
+ 
+#define WM_MSG_CLASS_PLATFORM	0x010
+#define WM_MSG_CLASS_SCSI	0x020
+#define WM_MSG_CLASS_CDROM      0x040
+#define WM_MSG_CLASS_DB		0x080
+#define WM_MSG_CLASS_MISC	0x100
+
+#define WM_MSG_CLASS_ALL	0xff0
+
+extern int wm_lib_verbosity;
+
+/*
+ * I did not know any better place...
+ */
+#ifdef DEBUG
+#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t );
+#else
+#define CHECKPOINT(t) 
+#endif
+
+#ifndef TRUE
+#define TRUE	1
+#endif
+
+#ifndef FALSE
+#define FALSE	0
+#endif
+
+#ifdef linux
+#include <signal.h>
+/* Linux doesn't have a SIGEMT */
+#if !defined( SIGEMT )
+#  define SIGEMT SIGUNUSED
+#endif
+#endif /* linux */
+
+void		freeup( char **x );
+void	 	wm_strmcat( char **t, char *s);
+void	 	wm_strmcpy( char **t, char *s );
+char *		wm_strdup( char *s );
+/* Somebody's version query unsatisfied? */
+int		wm_libver_major( void );	/* return major internal version number */
+int		wm_libver_minor( void );	/* return minor internal version number */
+int		wm_libver_pl( void );		/* return internal patchlevel number */
+char *		wm_libver_name( void );		/* return internal name (LibWorkMan) */
+char *		wm_libver_number( void );	/* returns string: "<major>.<minor>.<pl>" */
+char *		wm_libver_string( void ); 	/* returns string: "<name> <number>" */
+char *		wm_libver_date( void );		/* returns string: date of compilation */
+void 		wm_lib_set_verbosity( int level ); /* set verbosity level */
+int 		wm_lib_get_verbosity( void );      /* get verbosity level */
+void 		wm_lib_message( unsigned int level, char *format, ... ); /* put out a message on stderr */
+int		wm_susleep( int usec );
+
+#endif /* WM_HELPERS_H */
diff --git a/ascd/ascd/workman/wm_index.h b/ascd/ascd/workman/wm_index.h
new file mode 100644
index 0000000..ce14ac3
--- /dev/null
+++ b/ascd/ascd/workman/wm_index.h
@@ -0,0 +1,36 @@
+#ifndef WM_INDEX_H
+#define WM_INDEX_H
+/*
+ * $Id: wm_index.h,v 1.2 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes for wm_index.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+int	idx_find_entry( char *file, int ntracks, int *tracks,
+		 	int len, int fuzz, unsigned long *pos );
+int	idx_delete_entry(char *file, int track, int fuzz, unsigned long pos );
+int	idx_write_entry( char *file, int track, unsigned long pos );
+
+#endif /* WM_INDEX_H */
diff --git a/ascd/ascd/workman/wm_platform.h b/ascd/ascd/workman/wm_platform.h
new file mode 100644
index 0000000..03f957d
--- /dev/null
+++ b/ascd/ascd/workman/wm_platform.h
@@ -0,0 +1,51 @@
+#ifndef WM_PLATFORM_H
+#define WM_PLATFORM_H
+/*
+ * $Id: wm_platform.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * The platform interface
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+int wmcd_open( struct wm_drive *d );
+int wmcd_reopen( struct wm_drive *d );
+
+/*
+ * void	keep_cd_open( void );
+ */
+int	wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+         	 void *retbuf, int retbuflen, int getreply );
+
+/****************************************
+ *
+ * The drive prototypes.
+ *
+ */
+extern struct wm_drive generic_proto;
+extern struct wm_drive sony_proto;
+extern struct wm_drive toshiba_proto;
+
+#endif /* WM_PLATFORM_H */
diff --git a/ascd/ascd/workman/wm_scsi.h b/ascd/ascd/workman/wm_scsi.h
new file mode 100644
index 0000000..8763a62
--- /dev/null
+++ b/ascd/ascd/workman/wm_scsi.h
@@ -0,0 +1,48 @@
+#ifndef WM_SCSI_H
+#define WM_SCSI_H
+/*
+ * $Id: wm_scsi.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * SCSI prototypes (scsi.c)
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h" 
+
+#define	WM_ERR_SCSI_INQUIRY_FAILED -1
+
+int	wm_scsi_mode_sense( struct wm_drive *d, unsigned char page,
+			    unsigned char *buf );
+int	sendscsi( struct wm_drive *d, void *buf,
+		  unsigned int len, int dir,
+		  unsigned char a0, unsigned char a1,
+		  unsigned char a2, unsigned char a3,
+		  unsigned char a4, unsigned char a5,
+		  unsigned char a6, unsigned char a7,
+		  unsigned char a8, unsigned char a9,
+		  unsigned char a10, unsigned char a11 );
+ int	wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+				char *model, char *rev );
+
+#endif /* WM_SCSI_H */
diff --git a/ascd/ascd/workman/wm_struct.h b/ascd/ascd/workman/wm_struct.h
new file mode 100644
index 0000000..ae1b72a
--- /dev/null
+++ b/ascd/ascd/workman/wm_struct.h
@@ -0,0 +1,181 @@
+#ifndef WM_STRUCT_H
+#define WM_STRUCT_H
+/*
+ * $Id: wm_struct.h,v 1.7 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+
+/*
+ * Structure for a single track.  This is pretty much self-explanatory --
+ * one of these exists for each track on the current CD.
+ */
+struct wm_trackinfo 
+{
+  char	*songname;	/* Name of song, dynamically allocated */
+  char	*otherdb;	/* Unrecognized info for this track */
+  char	*otherrc;
+  int	length;		/* Length of track in seconds or Kbytes */
+  int	start;		/* Starting position (f+s*75+m*60*75) */
+  int	volume;		/* Per-track volume (1-32, 0 to disable) */
+  int	track;		/* Physical track number */
+  int	section;	/* Section number (0 if track not split) */
+  int	contd;		/* Flag: continuation of previous track */
+  int	avoid;		/* Flag: don't play this track. */
+  int	data;		/* Flag: data track */
+};
+
+/*
+ * Structure for internal playlist management.  The internal playlist is
+ * simply the list of track ranges that are being played currently.  This
+ * is built whenever the CD starts playing; it's used in normal and shuffle
+ * modes as well as playlist mode.
+ *
+ * The "starttime" element represents how much time has elapsed by the time
+ * we get to this entry.  For instance, if the list begins with a 5-minute
+ * track and a 3-minute track, the third entry would have a starttime of 8
+ * minutes.  This is used so that the elapsed play time can be displayed
+ * even in shuffle or playlist modes.
+ *
+ * The last member of the list has a start track of 0, and its starttime is
+ * the total playing time of the playlist (which will usually be overestimated,
+ * since we don't play leadouts in some cases.)
+ */
+struct wm_play 
+{
+  int	start;		/* Start track, or 0 if end of list */
+  int	end;		/* last track plus 1 */
+  int	starttime;	/* Number of seconds elapsed previously */
+};
+
+/*
+ * Structure for playlists (as seen by the user.)  This is simply a name
+ * followed by a zero-terminated list of track numbers to play.  The list
+ * is terminated by a NULL name.
+ */
+struct wm_playlist 
+{
+  char	*name;		/* Name of this playlist */
+  int	*list;		/* List of tracks */
+};
+
+struct wm_cdinfo
+{
+  char	artist[84];	/* Artist's name */
+  char	cdname[84];	/* Disc's name */
+  int	ntracks;	/* Number of tracks on the disc */
+  int	length;		/* Total running time in seconds */
+  int	autoplay;	/* Start playing CD immediately */
+  int	playmode;	/* How to play the CD */
+  int	volume;		/* Default volume (1-32, 0 for none) */
+  struct wm_trackinfo *trk;	/* struct wm_trackinfo[ntracks] */
+  struct wm_playlist *lists;	/* User-specified playlists */
+  char	*whichdb;	/* Which database is this entry from? */
+  char	*otherdb;	/* Unrecognized lines from this entry */
+  char	*otherrc;
+  char	*user;		/* Name of originating user */
+  unsigned int cddbid;  /* CDDB-ID of the current disc */
+  struct cdinfo *next;	/* For browsers, etc. */
+};
+
+/* The global variable "cd" points to the struct for the CD that's playing. */
+extern struct wm_cdinfo *cd;
+
+struct wm_playlist *new_list();
+
+enum wm_cd_modes	
+{
+  WM_CDM_UNKNOWN = -1,
+  WM_CDM_BACK = 0, WM_CDM_TRACK_DONE = 0,
+  WM_CDM_PLAYING = 1,
+  WM_CDM_FORWARD = 2,
+  WM_CDM_PAUSED = 3,
+  WM_CDM_STOPPED = 4,
+  WM_CDM_EJECTED = 5
+};
+
+/*
+ * Drive descriptor structure.  Used for access to low-level routines.
+ */
+struct wm_drive 
+{
+  int	fd;		/* File descriptor, if used by platform */
+  char	vendor[32];	/* Vendor name */
+  char	model[32];	/* Drive model */
+  char  revision[32];   /* Revision of the drive */
+  void	*aux;		/* Pointer to optional platform-specific info */
+  void	*daux;		/* Pointer to optional drive-specific info */
+  
+  int	(*init)();
+  int	(*get_trackcount)();
+  int	(*get_cdlen)();
+  int	(*get_trackinfo)();
+  int	(*get_drive_status)();
+  int	(*get_volume)();
+  int	(*set_volume)();
+  int	(*pause)();
+  int	(*resume)();
+  int	(*stop)();
+  int	(*play)();
+  int	(*eject)();
+  int   (*closetray)();
+};
+
+/*
+ * Structure for information of the usage of cddb.
+ */
+struct wm_cddb {
+        int     protocol;               /* 0-off, 1-cddbp, 2-http, 3-htproxy */
+        char    cddb_server[84];        /* host.domain.name:port */
+        char    mail_adress[84];        /* [email protected] */
+        char    path_to_cgi[84];        /* (/)path/to/cddb.cgi */
+        char    proxy_server[84];       /* host.domain.name:port */
+};
+extern struct wm_cddb cddb;
+
+
+/*
+ * Each platform has to define generic functions, so may as well declare
+ * them all here to save space.
+ * These functions should never be seen outside libworkman. So I don't care
+ * about the wm_ naming convention here.
+ */
+int     gen_init(),
+        gen_get_trackcount(),
+        gen_get_cdlen(),
+        gen_get_trackinfo(),
+	gen_get_drive_status(),
+	gen_get_volume(),
+	gen_set_volume(),
+	gen_pause(),
+	gen_resume(),
+	gen_stop(),
+	gen_play(),
+	gen_eject(),
+	gen_closetray();
+
+struct wm_drive *find_drive_struct();
+
+
+#endif /* WM_STRUCT_H */
diff --git a/ascd/ascd/workman/wm_version.h b/ascd/ascd/workman/wm_version.h
new file mode 100644
index 0000000..b18a2e2
--- /dev/null
+++ b/ascd/ascd/workman/wm_version.h
@@ -0,0 +1,35 @@
+#ifndef WM_VERSION_H
+#define WM_VERSION_H
+/*
+ * $Id: wm_version.h,v 1.2 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Version information
+ *
+ */
+
+#define WM_LIBVER_MAJOR	1
+#define WM_LIBVER_MINOR 4
+#define WM_LIBVER_PL	2
+#define WM_LIBVER_NAME  "LibWorkMan"
+
+#endif /* WM_VERSION_H */
diff --git a/ascd/ascd/workman/workman.h b/ascd/ascd/workman/workman.h
new file mode 100644
index 0000000..2172b9f
--- /dev/null
+++ b/ascd/ascd/workman/workman.h
@@ -0,0 +1,51 @@
+#ifndef WORKMAN_H
+#define WORKMAN_H
+/*
+ * $Id: workman.h,v 1.5 1999/05/05 16:34:22 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * all-in-one libworkman include file.
+ *
+ */
+
+
+/*
+ * wm_config should always be included first
+ */
+#include "wm_config.h" 
+
+#include "workman_defs.h"
+#ifdef BUILD_CDDA
+#include "wm_cdda.h"
+#endif 
+#include "wm_cddb.h"
+#include "wm_cdinfo.h" 
+#include "wm_cdrom.h" 
+#include "wm_database.h" 
+#include "wm_helpers.h" 
+#include "wm_index.h" 
+#include "wm_platform.h" 
+#include "wm_scsi.h" 
+#include "wm_struct.h" 
+
+#endif /* WORKMAN_H */
+
diff --git a/ascd/ascd/workman/workman_defs.h b/ascd/ascd/workman/workman_defs.h
new file mode 100644
index 0000000..712a414
--- /dev/null
+++ b/ascd/ascd/workman/workman_defs.h
@@ -0,0 +1,30 @@
+#ifndef WORKMAN_DEFS_H
+#define WORKMAN_DEFS_H
+/*
+ * $Id: workman_defs.h,v 1.4 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player program
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * 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
+ * (at your option) 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.
+ *
+ * #defined CONSTANTS
+ * 
+ * Too bad this file seems to be so empty...
+ *
+ */
+
+#include "wm_version.h"
+
+#endif /* WORKMAN_DEFS_H */
diff --git a/ascd/configure b/ascd/configure
new file mode 100755
index 0000000..d533465
--- /dev/null
+++ b/ascd/configure
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd ascd
+rm -f workman
+ln -s ../libworkman/include workman
+./configure
+cd ..
+xmkmf -a
+
diff --git a/ascd/default b/ascd/default
new file mode 100755
index 0000000..246ca52
--- /dev/null
+++ b/ascd/default
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+cd ascd
+rm -f workman
+ln -s ../libworkman/include workman
+./misc/default
+cd ..
+xmkmf -a
+
diff --git a/ascd/libworkman/COPYING.LIB b/ascd/libworkman/COPYING.LIB
new file mode 100644
index 0000000..92b8903
--- /dev/null
+++ b/ascd/libworkman/COPYING.LIB
@@ -0,0 +1,481 @@
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+    		    59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+		  GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library 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
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ascd/libworkman/Config b/ascd/libworkman/Config
new file mode 100644
index 0000000..7db9048
--- /dev/null
+++ b/ascd/libworkman/Config
@@ -0,0 +1,48 @@
+XCOMM -------------------------------------------------------------------
+XCOMM        libworkman installation configuration
+XCOMM -------------------------------------------------------------------
+XCOMM
+XCOMM This file is part of LibWorkMan, the civilized CD player library
+XCOMM (c) 1991-1997 by Steven Grimm (original author)
+XCOMM (c) by Dirk Försterling (current 'author' = maintainer)
+XCOMM The maintainer can be contacted by his e-mail address:
+XCOMM [email protected] 
+XCOMM
+XCOMM This library is free software; you can redistribute it and/or
+XCOMM modify it under the terms of the GNU Library General Public
+XCOMM License as published by the Free Software Foundation; either
+XCOMM version 2 of the License, or (at your option) any later version.
+XCOMM
+XCOMM This library is distributed in the hope that it will be useful,
+XCOMM but WITHOUT ANY WARRANTY; without even the implied warranty of
+XCOMM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+XCOMM Library General Public License for more details.
+XCOMM
+XCOMM You should have received a copy of the GNU Library General Public
+XCOMM License along with this library; if not, write to the Free
+XCOMM Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+XCOMM
+XCOMM -------------------------------------------------------------------
+XCOMM
+XCOMM This configures the compilation and installation
+XCOMM processes only. To configure libworkman to your
+XCOMM needs, please review the file include/wm_config.h
+XCOMM
+XCOMM
+
+XCOMM -------------------------------------------------------------------
+XCOMM                        Directory Prefix
+XCOMM -------------------------------------------------------------------
+DESTDIR=/usr/local/
+
+XCOMM -------------------------------------------------------------------
+XCOMM          LibWorkMan include file target relative to DSTDIR
+XCOMM -------------------------------------------------------------------
+WMINCDIR=include/workman
+
+
+
+XCOMM -------------------------------------------------------------------
+#define SHARED_LIBWORKMAN
+LIBDIR=lib
+
diff --git a/ascd/libworkman/Imakefile b/ascd/libworkman/Imakefile
new file mode 100644
index 0000000..c0f26ac
--- /dev/null
+++ b/ascd/libworkman/Imakefile
@@ -0,0 +1,74 @@
+XCOMM -------------------------------------------------------------------
+XCOMM                     libworkman Imakefile
+XCOMM -------------------------------------------------------------------
+XCOMM
+XCOMM This file is part of LibWorkMan, the civilized CD player library
+XCOMM (c) 1991-1997 by Steven Grimm (original author)
+XCOMM (c) by Dirk Försterling (current 'author' = maintainer)
+XCOMM The maintainer can be contacted by his e-mail address:
+XCOMM [email protected] 
+XCOMM
+XCOMM This library is free software; you can redistribute it and/or
+XCOMM modify it under the terms of the GNU Library General Public
+XCOMM License as published by the Free Software Foundation; either
+XCOMM version 2 of the License, or (at your option) any later version.
+XCOMM
+XCOMM This library is distributed in the hope that it will be useful,
+XCOMM but WITHOUT ANY WARRANTY; without even the implied warranty of
+XCOMM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+XCOMM Library General Public License for more details.
+XCOMM
+XCOMM You should have received a copy of the GNU Library General Public
+XCOMM License along with this library; if not, write to the Free
+XCOMM Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+XCOMM
+XCOMM -------------------------------------------------------------------
+XCOMM
+XCOMM Please edit the file "Config" for paths.
+XCOMM
+
+#include <Config>
+
+#include "include/wm_version.h"
+
+WM_LIBVER=WM_LIBVER_MAJOR.WM_LIBVER_MINOR.WM_LIBVER_PL
+
+SRCS = cddb.c cdrom.c cdinfo.c plat_freebsd.c plat_sun.c plat_hpux.c \
+       plat_ultrix.c plat_news.c plat_bsd386.c plat_osf1.c plat_linux.c \
+       plat_aix.c plat_openbsd.c plat_irix.c plat_svr4.c \
+       drv_sony.c drv_toshiba.c scsi.c database.c index.c \
+       wm_helpers.c
+
+OBJS = cddb.o cdrom.o cdinfo.o plat_freebsd.o plat_sun.o plat_hpux.o \
+       plat_ultrix.o plat_news.o plat_bsd386.o plat_osf1.o plat_linux.o \
+       plat_aix.o plat_openbsd.o plat_irix.o plat_svr4.o \
+       drv_sony.o drv_toshiba.o scsi.o database.o index.o \
+       wm_helpers.o
+
+HEADERS= \
+	include/wm_cdda.h \
+	include/wm_cdinfo.h \
+	include/wm_cdrom.h \
+	include/wm_config.h \
+	include/wm_database.h \
+	include/wm_helpers.h \
+	include/wm_index.h \
+	include/wm_platform.h \
+	include/wm_scsi.h \
+	include/wm_struct.h \
+	include/wm_version.h \
+	include/workman.h \
+	include/workman_defs.h 
+       
+
+#include <Library.tmpl>
+
+#ifdef SHARED_LIBWORKMAN
+SharedLibraryTarget(workman,$(WM_LIBVER),$(OBJS),.,.)
+InstallSharedLibrary(workman,$(WM_LIBVER),$(LIBDIR))
+#else
+NormalLibraryTarget(workman, $(OBJS))
+#endif
+
+InstallMultiple($(HEADERS),$(WMINCDIR))
+
diff --git a/ascd/libworkman/README b/ascd/libworkman/README
new file mode 100644
index 0000000..87854ac
--- /dev/null
+++ b/ascd/libworkman/README
@@ -0,0 +1,6 @@
+$Id: README,v 1.2 1999/02/14 09:53:54 dirk Exp $
+
+This directory contains the WorkMan library.
+
+libworkman is a multi-plaform CD-Player library for creating various
+CD-Player-UIs.
diff --git a/ascd/libworkman/buildindex.c b/ascd/libworkman/buildindex.c
new file mode 100644
index 0000000..05a042c
--- /dev/null
+++ b/ascd/libworkman/buildindex.c
@@ -0,0 +1,223 @@
+/*
+ * $Id: buildindex.c,v 1.2 1999/02/14 09:50:42 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Build a WorkMan database index file from a flat text file.  Requires
+ * 4.4BSD libdb library.
+ */
+
+static char buildindex_id[] = "$Id: buildindex.c,v 1.2 1999/02/14 09:50:42 dirk Exp $";
+ 
+#include <stdio.h>
+#include <db.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <netinet/in.h>		/* for htonl() */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+static char buildindex_id[]="$Id: buildindex.c,v 1.2 1999/02/14 09:50:42 dirk Exp $";
+
+char *strrchr();
+
+main(argc, argv)
+	int	argc;
+	char	**argv;
+{
+	DB	*db;
+	DBT	key, data;
+	FILE	*fp;
+	int	lock = 1, i = 0, locked, frame;
+	char	buf[1000], indname[MAXPATHLEN + 100], framebuf[8], *c;
+	unsigned long	pos;
+	BTREEINFO	bt;
+	struct stat	st;
+
+	if (argc > 2 && !strcmp(argv[1], "-n"))
+	{
+		lock = 0;
+		i++;
+	}
+
+	if (argc < i + 2)
+	{
+		fprintf(stderr, "Usage: %s [-n] dbfile [dbfile ...]\n",
+			argv[0]);
+		exit(1);
+	}
+
+	data.data = &pos;
+	data.size = sizeof(pos);
+	key.data = framebuf;
+	key.size = 7;		/* %07d */
+
+	while (++i < argc)
+	{
+		fprintf(stderr, "Building index for %s\n", argv[i]);
+
+		if ((fp = fopen(argv[i], "r")) == NULL)
+		{
+			perror(argv[i]);
+			continue;
+		}
+
+		/*
+		 * Figure out the file's mode, uid, gid, so we can set the
+		 * permissions on the index file to the same thing.
+		 */
+		if (fstat(fileno(fp), &st))
+		{
+			sprintf(indname, "%s: fstat", argv[i]);
+			perror(indname);
+			fclose(fp);
+			continue;
+		}
+
+		if (lock && lockit(fileno(fp), F_WRLCK))
+		{
+			sprintf(indname, "%s: Warning: Couldn't lock", argv[i]);
+			perror(indname);
+			locked = 0;
+		}
+		else
+			locked = 1;
+
+		/*
+		 * Create a database file.
+		 */
+		bt.flags = R_DUP;	/* allow duplicate keys */
+		bt.cachesize = 0;
+		bt.psize = 0;
+		bt.lorder = 4321;
+		bt.minkeypage = 0;
+		bt.compare = NULL;	/* use lexical comparison */
+		bt.prefix = NULL;	/* no prefix comparisons */
+
+		/* Index files have ".ind" extensions */
+		sprintf(indname, "%s.ind", argv[i]);
+		if ((db = dbopen(indname, O_CREAT | O_RDWR | O_TRUNC,
+			st.st_mode, DB_BTREE, &bt)) == NULL)
+		{
+			perror(indname);
+			if (locked)
+				lockit(fileno(fp), F_UNLCK);
+			fclose(fp);
+			continue;
+		}
+
+		/*
+		 * Now loop through the text file, inserting a record into
+		 * the index file for each "tracks" line.
+		 */
+		while (! feof(fp))
+		{
+			pos = ftell(fp);
+			buf[0] = '\0';
+			if (fgets(buf, sizeof(buf), fp) == NULL || ! buf[0])
+			{
+				/* End of file? */
+				if (feof(fp))
+					break;
+				
+				/* Nope.  A read error.  Unlink the database. */
+				perror(argv[i]);
+				(void) unlink(indname);
+				break;
+			}
+
+			if (strncmp(buf, "tracks ", 7))
+				continue;
+			
+			/*
+			 * Found the start of a record.  Figure out the start
+			 * time of the last track and put an entry in the
+			 * index file with that as the key.
+			 */
+			c = strrchr(buf, ' ');	/* this will always succeed */
+			*c = '\0';
+			c = strrchr(buf, ' ');	/* this should too, but... */
+			if (c == NULL)
+			{
+				fprintf(stderr,
+					"%s: Malformed tracks line at %lu\n",
+					argv[i], pos);
+				continue;
+			}
+			sscanf(c+1, "%d", &frame);
+			sprintf(framebuf, "%07d", frame);
+			pos = htonl(pos);
+
+			if ((db->put)(db, &key, &data, 0))
+			{
+				perror(indname);
+				unlink(indname);
+				break;
+			}
+		}
+
+		/*
+		 * Clean up.
+		 */
+		(void) (db->close)(db);
+		if (locked)
+			lockit(fileno(fp), F_UNLCK);
+	}
+}
+
+/*
+ * Lock a file.  Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(fd, type)
+	int	fd;
+	int	type;
+{
+	struct flock	fl;
+	int		result, timer = 0;
+
+	fl.l_type = type;
+	fl.l_whence = 0;
+	fl.l_start = 0;
+	fl.l_len = 0;
+
+	while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+	{
+		if (errno != EACCES || errno != EAGAIN)
+			break;
+		if (timer++ == 30)
+		{
+			errno = ETIMEDOUT;
+			break;
+		}
+
+		sleep(1);
+	}
+
+	return (result);
+}
diff --git a/ascd/libworkman/cddaslave.c b/ascd/libworkman/cddaslave.c
new file mode 100644
index 0000000..bd85084
--- /dev/null
+++ b/ascd/libworkman/cddaslave.c
@@ -0,0 +1,565 @@
+/*
+ * $id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ******************************************************************
+ *
+ * Digital audio manipulator for WorkMan.
+ *
+ * The CDDA architecture looks like this:
+ *
+ *                     WorkMan  (or another UI!)
+ *                       ^^^
+ *                       |||    (separate processes connected by pipe)
+ *                       vvv
+ *     +------------- cddaslave -------------+
+ *     |                  |                  |
+ * command module    CDDA reader       audio output
+ * (portable)        (per platform)    (per platform)
+ *
+ * This source file has the command module and some of the scaffolding
+ * to hold cddaslave together, plus some non-system-dependent audio
+ * processing code.  Look in plat_*_cdda.c for system-specific stuff.
+ *
+ */
+
+#include "config.h"
+
+#ifdef BUILD_CDDA
+ 
+static char cddaslave_id[] = "$Id: cddaslave.c,v 1.3 1999/02/14 22:10:24 dirk Exp $";
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include "include/wm_cdda.h"
+#include "include/wm_platform.h"
+
+
+int	playing = 0;		/* Should the CD be playing now? */
+
+/*
+ * Loudness setting, plus the floating volume multiplier and decaying-average
+ * volume level.
+ */
+int		loudness = 0;
+unsigned int	volume = 32768;
+unsigned int	level;
+
+/*
+ * Playback speed (0 = slow)
+ */
+int		speed = 128;
+
+/*
+ * This is non-null if we're saving audio to a file.
+ */
+FILE		*output = NULL;
+
+/*
+ * Audio file header format.
+ */
+typedef unsigned long	u_32;
+struct auheader {
+	u_32	magic;
+	u_32	hdr_size;
+	u_32	data_size;
+	u_32	encoding;
+	u_32	sample_rate;
+	u_32	channels;
+};
+
+/* had to change #ifdef to #if   -> see wm_cdda.h */
+#if WM_BIG_ENDIAN
+# ifndef htonl
+#  define htonl(x) (x)
+# endif
+#else
+extern unsigned long htonl(x);
+#endif
+
+void *malloc();
+long cdda_transform();
+
+/*
+ * Send status information upstream.
+ */
+void
+send_status(blk)
+	struct cdda_block	*blk;
+{
+	write(1, blk, sizeof(*blk));
+}
+
+/*
+ * Accept a command from our master.
+ *
+ * The protocol is byte-oriented:
+ *   PmsfMSFxyz		Play from msf to MSF (MSF can be 0,0,0 to play to end)
+ *			xyz is the msf of the start of this chunk, i.e., the
+ *			ending boundary for reverse play.
+ *   S                  Stop.
+ *   E                  Eject.  This means we just close the CD device and
+ *                      open it again later.
+ *   Q			Quit.
+ *   Vn 		Set volume level (0-255).
+ *   Bn			Set balance level (0-255).
+ *   EnL		Set an equalizer level (n = 0 for bass, 255 for treble)
+ *   G			Get current status.
+ *   sn                 Set speed multiplier to n.
+ *   dn			Set direction to forward (n = 0) or reverse.
+ *   Fllllx...          Start saving to a file (length = l, followed by name)
+ *   F0000              Stop saving.
+ *   Ln			Set loudness level (0-255).
+ */
+int
+command(cd_fd, blk)
+	int			cd_fd;
+	struct cdda_block	*blk;
+{
+	unsigned char		inbuf[10];
+	char			*filename;
+	int			namelen;
+	struct auheader		hdr;
+
+	if (read(0, inbuf, 1) <= 0)	/* Parent died. */
+	{
+		wmcdda_close();
+		wmaudio_close();
+		exit(0);
+	}
+
+	switch (inbuf[0]) {
+	case 'E':
+		playing = 0;
+		wmaudio_stop();
+		wmcdda_close(cd_fd);
+		cd_fd = -1;
+	        blk->status = WMCDDA_ACK;
+	        send_status(blk);
+		break;
+	case 'P':
+		read(0, inbuf, 9);
+		playing = 1;
+
+		wmaudio_stop();
+
+		wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2],
+			inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5],
+			inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]);
+
+		wmaudio_ready();
+
+		level = 2500;
+		volume = 1 << 15;
+
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'S':
+		playing = 0;
+		wmaudio_stop();
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		blk->status = WMCDDA_STOPPED;
+		send_status(blk);
+		break;
+
+	case 'B':
+		read(0, inbuf, 1);
+		wmaudio_balance(inbuf[0]);
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'V':
+		read(0, inbuf, 1);
+		wmaudio_volume(inbuf[0]);
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'G':
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+
+		if (playing)
+			blk->status = WMCDDA_PLAYED;
+		else
+			blk->status = WMCDDA_STOPPED;
+		wmaudio_state(blk);
+		send_status(blk);
+		break;
+
+	case 'Q':
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		wmcdda_close();
+		wmaudio_close();
+		exit(0);
+
+	case 's':
+		read(0, inbuf, 1);
+		speed = inbuf[0];
+		wmcdda_speed(speed);
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'd':
+		read(0, inbuf, 1);
+		wmcdda_direction(inbuf[0]);
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'L':
+		read(0, inbuf, 1);
+		loudness = inbuf[0];
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+		break;
+
+	case 'F':
+		read(0, &namelen, sizeof(namelen));
+		if (output != NULL)
+		{
+			fclose(output);
+			output = NULL;
+		}
+		if (namelen)
+		{
+			filename = malloc(namelen + 1);
+			if (filename == NULL)
+			{
+				perror("cddaslave");
+				wmcdda_close();
+				wmaudio_close();
+				exit(1);
+			}
+
+			read(0, filename, namelen);
+			filename[namelen] = '\0';
+			output = fopen(filename, "w");
+			if (output == NULL)
+				perror(filename);
+			else
+			{
+				/* Write an .au file header. */
+				hdr.magic = htonl(0x2e736e64);
+				hdr.hdr_size = htonl(sizeof(hdr) + 28);
+				hdr.data_size = htonl(~0);
+				hdr.encoding = htonl(3);	/* linear-16 */
+				hdr.sample_rate = htonl(44100);
+				hdr.channels = htonl(2);
+
+				fwrite(&hdr, sizeof(hdr), 1, output);
+				fwrite("Recorded from CD by WorkMan", 28, 1,
+					output);
+			}
+
+			free(filename);
+		}
+
+		blk->status = WMCDDA_ACK;
+		send_status(blk);
+	}
+	return(cd_fd);
+}
+
+/*
+ * Transform some CDDA data.
+ */
+long
+wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+	long		i;
+	long		*buf32 = (long *)rawbuf;
+	register short	*buf16 = (short *)rawbuf;
+	register int	aval;
+
+	/*
+	 * Loudness transformation.  Basically this is a self-adjusting
+	 * volume control; our goal is to keep the average output level
+	 * around a certain value (2500 seems to be pleasing.)  We do this
+	 * by maintaining a decaying average of the recent output levels
+	 * (where "recent" is some fraction of a second.)  All output levels
+	 * are multiplied by the inverse of the decaying average; this has
+	 * the volume-leveling effect we desire, and isn't too CPU-intensive.
+	 *
+	 * This is done by modifying the digital data, rather than adjusting
+	 * the system volume control, because (at least on some systems)
+	 * tweaking the system volume can generate little pops and clicks.
+	 *
+	 * There's probably a more elegant way to achieve this effect, but
+	 * what the heck, I never took a DSP class and am making this up as
+	 * I go along, with a little help from some friends.
+	 *
+	 * This is all done with fixed-point math, oriented around powers
+	 * of two, which with luck will keep the CPU usage to a minimum.
+	 * More could probably be done, for example using lookup tables to
+	 * replace multiplies and divides; whether the memory hit (128K
+	 * for each table) is worthwhile is unclear.
+	 */
+	if (loudness)
+	{
+		/* We aren't really going backwards, but i > 0 is fast */
+		for (i = buflen / 2; i > 0; i--)
+		{
+			/*
+			 * Adjust this sample to the current level.
+			 */
+			aval = (*buf16 = (((long)*buf16) * volume) >> 15);
+			buf16++;
+
+			/*
+			 * Don't adjust the decaying average for each sample;
+			 * that just spends CPU time for very little benefit.
+			 */
+			if (i & 127)
+				continue;
+
+			/*
+			 * We want to use absolute values to compute the
+			 * decaying average; otherwise it'd sit around 0.
+			 */
+			if (aval < 0)
+				aval = -aval;
+
+			/*
+			 * Adjust more quickly when we start hitting peaks,
+			 * or we'll get clipping when there's a sudden loud
+			 * section after lots of quiet.
+			 */
+			if (aval & ~8191)
+				aval <<= 3;
+
+			/*
+			 * Adjust the decaying average.
+			 */
+			level = ((level << 11) - level + aval) >> 11;
+
+			/*
+			 * Let *really* quiet sounds play softly, or we'll
+			 * amplify background hiss to full volume and blast
+			 * the user's speakers when real sound starts up.
+			 */
+			if (! (level & ~511))
+				level = 512;
+
+			/*
+			 * And adjust the volume setting using the inverse
+			 * of the decaying average.
+			 */
+			volume = (2500 << 15) / level;
+		}
+	}
+
+	if (speed == 128)
+		return (buflen);
+
+	/*
+	 * Half-speed play.  Stretch things out.
+	 */
+	if (speed == 0)
+	{
+		buflen /= 2;	/* Since we're moving 32 bits at a time */
+
+		for (i = buflen - 1; i > 0; i--)
+		{
+			buf32[i] = buf32[i / 2];
+		}
+
+		buflen *= 4;	/* 2 for doubling the buffer, 2 from above */
+	}
+
+	/*
+	 * Slow play; can't optimize it as well as half-speed.
+	 */
+	if (speed && speed < 128)
+	{
+		int	multiplier = ((speed + 128) * 128) / 256;
+		int	newlen;
+		int	tally = 0, pos;
+
+		buflen /= 4;	/* Get the number of 32-bit values */
+
+		/*
+		 * Buffer length doubles when speed is 0, stays the same
+		 * when speed is 128.
+		 */
+		newlen = (buflen * 128) / multiplier;
+
+		pos = buflen - 1;
+		for (i = newlen - 1; i > 0; i--)
+		{
+			buf32[i] = buf32[pos];
+			tally += multiplier;
+			if (tally & 128)
+			{
+				pos--;
+				tally ^= 128;
+			}
+		}
+
+		buflen = newlen * 4;
+	}
+
+	return (buflen);
+}
+
+
+main(argc, argv)
+	char	**argv;
+{
+	int			cd_fd = 3;
+	fd_set			readfd, dummyfd;
+	struct timeval		timeout;
+	char			*cddabuf;
+	long			cddabuflen;
+	struct cdda_block	blockinfo;
+	long			result;
+	int			nfds;
+	char			*devname;
+
+	/*
+	 * Device name should be the command-line argument.
+	 */
+	if (argc < 2)
+		devname = "";
+	else
+		devname = argv[1];
+
+	/*
+	 * If we're running setuid root, bump up our priority then lose
+	 * superuser access.
+	 */
+	nice(-14);
+	setuid(getuid());
+
+	FD_ZERO(&dummyfd);
+	FD_ZERO(&readfd);
+
+	timerclear(&timeout);
+
+	cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname);
+	if (cd_fd < 0)
+		exit(1);
+	wmaudio_init();
+
+	blockinfo.status = WMCDDA_ACK;
+	send_status(&blockinfo);
+	blockinfo.status = WMCDDA_STOPPED;
+
+	/*
+	 * Accept commands as they come in, and play some sound if we're
+	 * supposed to be doing that.
+	 */
+	while (1)
+	{
+		FD_SET(0, &readfd);
+
+		/*
+		 * If we're playing, we don't want select to block.  Otherwise,
+		 * wait a little while for the next command.
+		 */
+		if (playing)
+			timeout.tv_usec = 0;
+		else
+			timeout.tv_usec = 500000;
+
+		nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout);
+
+		if (nfds < 0)	/* Broken pipe; our GUI exited. */
+		{
+			wmcdda_close(cd_fd);
+			wmaudio_close();
+			exit(0);
+		}
+
+		if (FD_ISSET(0, &readfd))
+		{
+			/* If this doesn't work, just hope for the best */
+			if(cd_fd == -1)
+			    cd_fd = wmcdda_open(devname);
+			cd_fd = command(cd_fd, &blockinfo);
+			/*
+			 * Process all commands in rapid succession, rather
+			 * than possibly waiting for a CDDA read.
+			 */
+			continue;
+		}
+		
+		if (playing)
+		{
+			result = wmcdda_read(cd_fd, cddabuf, cddabuflen,
+				&blockinfo);
+			if (result <= 0)
+			{
+				/* Let the output queue drain. */
+				if (blockinfo.status == WMCDDA_DONE)
+				{
+					wmaudio_mark_last();
+					if (wmaudio_send_status())
+					{
+						/* queue drained, stop polling*/
+						playing = 0;
+					}
+				}
+				else
+				{
+					playing = 0;
+					send_status(&blockinfo);
+				}
+			}
+			else
+			{
+				result = wmcdda_normalize(cddabuf, result,
+							&blockinfo);
+				result = wmcdda_transform(cddabuf, result,
+							&blockinfo);
+				if (output)
+					fwrite(cddabuf, result, 1, output);
+				result = wmaudio_convert(cddabuf, result,
+							&blockinfo);
+				if (wmaudio_play(cddabuf, result, &blockinfo))
+				{
+					playing = 0;
+					wmaudio_stop();
+					send_status(&blockinfo);
+				}
+			}
+		}
+		else
+			send_status(&blockinfo);
+	}
+}
+
+#else /* BUILD_CDDA */
+
+main()
+{
+	exit(0);
+}
+
+#endif /* BUILD_CDDA */
diff --git a/ascd/libworkman/cddb.c b/ascd/libworkman/cddb.c
new file mode 100644
index 0000000..75e891b
--- /dev/null
+++ b/ascd/libworkman/cddb.c
@@ -0,0 +1,541 @@
+/*
+ * $Id: cddb.c,v 1.2 1999/02/14 09:50:42 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * establish connection to cddb server and get data
+ * socket stuff gotten from gnu port of the finger command
+ *
+ * provided by Sven Oliver Moll
+ *
+ */
+
+static char cddb_id[] = "$Id: cddb.c,v 1.2 1999/02/14 09:50:42 dirk Exp $";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_helpers.h"
+
+/*
+ * This is for identifying WorkMan at CDDB servers
+ */
+#define PROGRAM WORKMAN_NAME
+#define VERSION WORKMAN_VERSION
+
+struct wm_cddb cddb;
+
+int cur_cddb_protocol;
+char *cur_cddb_server;
+char *cur_cddb_mail_adress;
+char *cur_cddb_path_to_cgi;
+char *cur_cddb_proxy_server;
+
+int Socket;
+FILE *Connection;
+
+/*
+ *
+ */
+void
+cddb_cur2struct(void)
+{
+	cddb.protocol = cur_cddb_protocol;
+	strcpy(cddb.cddb_server,  cur_cddb_server);
+	strcpy(cddb.mail_adress,  cur_cddb_mail_adress);
+	strcpy(cddb.path_to_cgi,  cur_cddb_path_to_cgi);
+	strcpy(cddb.proxy_server, cur_cddb_proxy_server);
+} /* cddb_cur2struct() */
+
+/*
+ *
+ */
+void
+cddb_struct2cur(void)
+{
+	cur_cddb_protocol = cddb.protocol;
+	cur_cddb_server =       (cddb.cddb_server);
+	cur_cddb_mail_adress =  (cddb.mail_adress);
+	cur_cddb_path_to_cgi =  (cddb.path_to_cgi);
+	cur_cddb_proxy_server = (cddb.proxy_server);
+} /* cddb_struct2cur() */
+
+
+/*
+ * Subroutine from cddb_discid
+ */
+int
+cddb_sum(int n)
+{
+	char	buf[12],
+		*p;
+	int	ret = 0;
+
+	/* For backward compatibility this algorithm must not change */
+	sprintf(buf, "%lu", (unsigned long)n);
+	for (p = buf; *p != '\0'; p++)
+	  ret += (*p - '0');
+
+	return (ret);
+} /* cddb_sum() */
+
+
+/*
+ * Calculate the discid of a CD according to cddb
+ */
+unsigned long
+cddb_discid(void)
+{
+	int	i,
+		t,
+		n = 0;
+
+	/* For backward compatibility this algorithm must not change */
+	for (i = 0; i < thiscd.ntracks; i++) {
+	    
+		n += cddb_sum(thiscd.trk[i].start / 75);
+	/* 
+	 * Just for demonstration (See below)
+	 * 
+	 *	t += (thiscd.trk[i+1].start / 75) -
+	 *	     (thiscd.trk[i  ].start / 75);
+	 */	    
+	}
+
+	/*
+	 * Mathematics can be fun. Example: How to reduce a full loop to
+	 * a simple statement. The discid algorhythm is so half-hearted
+	 * developed that it doesn't even use the full 32bit range.
+	 * But it seems to be always this way: The bad standards will be
+	 * accepted, the good ones turned down.
+	 */
+
+	t = (thiscd.trk[thiscd.ntracks].start-thiscd.trk[0].start)/75;
+	return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks);
+} /* cddb_discid() */
+
+/*
+ * Split a string into two components according to the first occurance of
+ * the delimiter.
+ */
+char *
+string_split(char *line, char delim)
+{
+	char *p1;
+
+	for (p1=line;*p1;p1++)
+	{
+		if(*p1 == delim)
+		{
+			*p1 = 0;
+			return ++p1;
+		}
+	}
+	return (NULL);
+} /* string_split() */
+
+/*
+ * Generate the hello string according to the cddb protocol
+ * delimiter is either ' ' (cddbp) or '+' (http)
+ */
+void
+string_makehello(char *line,char delim)
+{
+	char mail[84],*host;
+	
+	strcpy(mail,cddb.mail_adress);
+	host=string_split(mail,'@');
+	
+	sprintf(line,"%shello%c%s%c%s%c%s%c%s",
+		delim == ' ' ? "cddb " : "&",
+		delim == ' ' ? ' ' : '=',
+		mail,delim,
+		host,delim,
+		PROGRAM,delim,
+		VERSION);
+} /* string_makehello() */
+
+/*
+ * Open the TCP connection to the cddb/proxy server
+ */
+int
+connect_open(void)
+{
+	char *host;
+	struct hostent *hp;
+	struct sockaddr_in soc_in;
+	int port;
+	
+	if(cddb.protocol == 3) /* http proxy */
+	  host = strdup(cddb.proxy_server);
+	else
+	  host = strdup(cddb.cddb_server);
+	/*	
+	 * t=string_split(host,':'); 
+	 */
+	port=atoi(string_split(host,':'));
+	if(!port)
+	  port=8880;
+
+	printf("%s:%d\n",host,port);
+	hp  =gethostbyname(host);
+  
+	if (hp == NULL)
+	{
+		static struct hostent def;
+		static struct in_addr defaddr;
+		static char *alist[1];
+		static char namebuf[128];
+		int inet_addr();
+		
+		defaddr.s_addr = inet_addr(host);
+		if (defaddr.s_addr == -1) 
+		{
+			printf("unknown host: %s\n", host);
+			return (-1);
+		}
+		strcpy(namebuf, host);
+		def.h_name = namebuf;
+		def.h_addr_list = alist, def.h_addr = (char *)&defaddr;
+		def.h_length = sizeof (struct in_addr);
+		def.h_addrtype = AF_INET;
+		def.h_aliases = 0;
+		hp = &def;
+	}
+	soc_in.sin_family = hp->h_addrtype;
+	bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length);
+	soc_in.sin_port = htons(port);
+	Socket = socket(hp->h_addrtype, SOCK_STREAM, 0);
+	if (Socket < 0) 
+	{ 
+		perror("socket");
+		return (-1);
+	}
+	fflush(stdout);
+	if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0) 
+	{   
+		perror("connect");
+		close(Socket);
+		return (-1);
+	}
+	
+	Connection = fdopen(Socket, "r");
+	return (0);
+} /* connect_open() */
+
+
+/*
+ * Close the connection
+ */
+void
+connect_close(void)
+{	   
+	(void)fclose(Connection);
+	close(Socket);
+} /* connect_close() */
+
+/*
+ * Get a line from the connection with CR and LF stripped
+ */
+void
+connect_getline(char *line)
+{
+	char c;
+    
+	while ((c = getc(Connection)) != '\n')
+	{
+		*line = c;
+		if ((c != '\r') && (c != (char)0xff))
+		  line++;
+	}
+	*line=0;
+} /* connect_getline() */
+
+/*
+ * Read the CD data from the server and place them into the cd struct
+ */
+void
+connect_read_entry(void)
+{
+	char type;
+	int trknr;
+	
+	char *t,*t2,tempbuf[2000];
+	
+	while(strcmp(tempbuf,"."))
+	{
+		connect_getline(tempbuf);
+		
+		t=string_split(tempbuf,'=');
+		if(t != NULL)
+		{
+			type=tempbuf[0];
+			
+			if(strncmp("TITLE",tempbuf+1,5))
+			  continue;
+			
+			if('D' == type)
+			{
+				/*
+				 * Annahme: "Interpret / Titel" ist falsch.
+				 * Daher: NULL-String erwarten.
+                                 */
+				t2=string_split(t,'/');
+				if(t2 == NULL)
+					t2 = t;
+				if(*t2 == ' ')
+				  t2++;
+				strcpy(cd->cdname,t2);
+				
+				for(t2=t;*t2;t2++)
+				{
+					if((*t2 == ' ') && (*(t2+1) == 0))
+					  *t2=0;
+				}
+				strcpy(cd->artist,t);
+			}
+			
+			if('T' == type)
+			{
+				trknr=atoi(tempbuf+6);
+				/*
+				 * printf("Track %d:%s\n",trknr,t);
+				 */
+				wm_strmcpy(&cd->trk[trknr].songname,t);
+			}
+			/*
+			 * fprintf(stderr, "%s %s\n",tempbuf,t);
+			 */
+		}
+	}
+} /* connect_read_entry() */
+
+/*
+ * Send a command to the server using cddbp
+ */
+void
+cddbp_send(char *line)
+{
+	write(Socket, line, strlen(line));
+	write(Socket, "\n", 1);
+} /* cddbp_send() */
+
+/*
+ * Send the "read from cddb" command to the server using cddbp
+ */
+void
+cddbp_read(char *category, unsigned int id)
+{
+	char tempbuf[84];
+	sprintf(tempbuf, "cddb read %s %08x", category, id);
+	cddbp_send(tempbuf);
+} /* cddbp_read() */
+
+/*
+ * Send a command to the server using http
+ */
+void
+http_send(char* line)
+{
+	char tempbuf[2000];
+	
+	write(Socket, "GET ", 4);
+	printf("GET ");
+	if(cddb.protocol == 3)
+	{
+		write(Socket, "http://", 7);
+		write(Socket, cddb.cddb_server, strlen(cddb.cddb_server));
+		printf("http://%s",cddb.cddb_server);
+	}
+	write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi));
+	write(Socket, "?cmd=" ,5);
+	write(Socket, line, strlen(line));
+	printf("%s?cmd=%s",cddb.path_to_cgi,line);
+	string_makehello(tempbuf,'+');
+	write(Socket, tempbuf, strlen(tempbuf));
+	printf("%s",tempbuf);
+	write(Socket, "&proto=1 HTTP/1.0\n\n", 19);
+	printf("&proto=1 HTTP/1.0\n");
+	do
+	  connect_getline(tempbuf);
+	while(strcmp(tempbuf,""));
+} /* http_send() */
+
+/*
+ * Send the "read from cddb" command to the server using http
+ */
+void
+http_read(char *category, unsigned int id)
+{
+	char tempbuf[84];
+	sprintf(tempbuf, "cddb+read+%s+%08x", category, id);
+	http_send(tempbuf);
+} /* http_read() */
+
+/*
+ * The main routine called from the ui
+ */
+void
+cddb_request(void)
+{
+	int i;
+	char tempbuf[2000];
+	extern int cur_ntracks;
+	
+	int status;
+	char category[20];
+	unsigned int id;
+	
+	strcpy(cddb.cddb_server,"localhost:888");
+	strcpy(cddb.mail_adress,"[email protected]");
+	/*
+	 * cddb.protocol = 1;
+	 */
+	wipe_cdinfo();
+	
+	switch(cddb.protocol)
+	{
+	 case 1: /* cddbp */
+		printf("USING CDDBP\n");
+		printf("open\n");
+		connect_open();
+		connect_getline(tempbuf);
+		printf("[%s]\n",tempbuf);
+		/*
+		 * if(atoi(tempbuf) == 201) return;
+		 */
+
+		/*
+		 * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins");
+		 */
+		string_makehello(tempbuf,' ');
+		fprintf(stderr, "%s\n", tempbuf);
+		cddbp_send(tempbuf);
+		connect_getline(tempbuf);
+		printf("[%s]\n",tempbuf);
+		
+		printf("query\n");
+		sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks);
+		for (i = 0; i < cur_ntracks; i++)
+		  if (thiscd.trk[i].section < 2)
+		    sprintf(tempbuf + strlen(tempbuf), " %d",
+			    thiscd.trk[i].start);
+		sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length);
+		printf(">%s<\n",tempbuf);
+		cddbp_send(tempbuf);
+		connect_getline(tempbuf);
+		printf("[%s]\n",tempbuf);
+		
+		status=atoi(tempbuf);
+		/*
+		 * fprintf(stderr, "status:%d\n",status);
+		 * fprintf(stderr,"category:%s\n",category);
+		 * fprintf(stderr,"id:%s\n",id);
+		 */
+		if(status == 200) /* Exact match */
+		{
+			sscanf(tempbuf,"%d %s %08x",&status,category,&id);
+			cddbp_read(category,id);
+			connect_read_entry();
+		}
+		
+		if(status == 211) /* Unexact match, multiple possible
+				   * Hack: always use first. */
+		{
+			connect_getline(tempbuf);
+			sscanf(tempbuf,"%s %08x",category,&id);
+			while(strcmp(tempbuf,"."))
+			  connect_getline(tempbuf);
+			cddbp_read(category,id);
+			connect_read_entry();
+		}
+		
+		cddbp_send("quit");
+		connect_close();
+		printf("close\n");
+		break;
+	 case 2: /* http */
+	 case 3: /* http proxy */
+		printf("USING HTTP%s\n",
+		       (cddb.protocol == 3) ? " WITH PROXY" : "");
+		printf("query\n");
+		sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks);
+		for (i = 0; i < cur_ntracks; i++)
+		  if (thiscd.trk[i].section < 2)
+		    sprintf(tempbuf + strlen(tempbuf), "+%d",
+			    thiscd.trk[i].start);
+		sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length);
+		printf(">%s<\n",tempbuf);
+		connect_open();
+		http_send(tempbuf);
+		connect_getline(tempbuf);
+		printf("[%s]\n",tempbuf);
+		
+		status=atoi(tempbuf);
+		/*
+		 * fprintf(stderr, "status:%d\n",status);
+		 * fprintf(stderr, "category:%s\n",category);
+		 * fprintf(stderr, "id:%s\n",id);
+		 */
+
+		if(status == 200) /* Exact match */
+		{
+			connect_close();
+			connect_open();
+			sscanf(tempbuf,"%d %s %08x",&status,category,&id);
+			http_read(category,id);
+			connect_read_entry();
+		}
+		
+		if(status == 211) /* Unexact match, multiple possible
+				   * Hack: always use first. */
+		{
+			connect_getline(tempbuf);
+			sscanf(tempbuf,"%s %08x",category,&id);
+			while(strcmp(tempbuf,"."))
+			  connect_getline(tempbuf);
+			connect_close();
+			connect_open();
+			http_read(category,id);
+			connect_read_entry();
+		}
+		/* moved close above break */
+		connect_close();
+		break;
+	 default: /* off */
+		break;
+	}
+} /* cddb_request() */
+
diff --git a/ascd/libworkman/cdinfo.c b/ascd/libworkman/cdinfo.c
new file mode 100644
index 0000000..525e227
--- /dev/null
+++ b/ascd/libworkman/cdinfo.c
@@ -0,0 +1,887 @@
+/*
+ * $Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $
+ * 
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Get information about a CD.
+ */
+
+static char cdinfo_id[] = "$Id: cdinfo.c,v 1.6 1999/02/14 16:47:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "include/wm_config.h"
+
+#include "include/wm_struct.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_database.h"
+#include "include/wm_helpers.h"
+
+struct wm_play *playlist = NULL;
+struct wm_cdinfo thiscd, *cd = &thiscd;
+
+int	cur_track = -1;	/* Current track number, starting at 1 */
+int	cur_index = 0;	/* Current index mark */
+int	cur_lasttrack = 999;	/* Last track to play in current chunk */
+int	cur_firsttrack = 0;	/* First track of current chunk */
+int	cur_pos_abs;	/* Current absolute position in seconds */
+int	cur_frame;	/* Current frame number */
+int	cur_pos_rel;	/* Current track-relative position in seconds */
+int	cur_tracklen;	/* Length in seconds of current track */
+int	cur_cdlen;	/* Length in seconds of entire CD */
+int	cur_ntracks;	/* Number of tracks on CD (= tracks + sections) */
+int	cur_nsections;	/* Number of sections currently defined */
+enum wm_cd_modes	cur_cdmode = WM_CDM_EJECTED;
+int	cur_listno;	/* Current index into the play list, if playing */
+char *	cur_artist;	/* Name of current CD's artist */
+char *	cur_cdname;	/* Album name */
+char *	cur_trackname;	/* Take a guess */
+char	cur_contd;	/* Continued flag */
+char	cur_avoid;	/* Avoid flag */
+
+int	exit_on_eject = 0;
+
+int cur_stopmode = -1;
+extern int info_modified;
+
+/*
+ * insert_trackinfo()
+ *
+ * Add a new track to the CD info structure.  Pass the position of the new
+ * entry in the track list -- 0 will make this the first track, 1 the second,
+ * etc.  The new entry will be zeroed out.
+ */
+void
+insert_trackinfo(num)
+	int	num;
+{
+	struct wm_trackinfo *newtrk;
+
+	/* Easy case: the list is empty */
+	if (cd->trk == NULL) {
+		if ((cd->trk = (struct wm_trackinfo *) calloc(1,
+						sizeof(*newtrk))) == NULL)
+		{
+nomem:
+			perror("insert_trackinfo");
+			exit(1);
+		} else {
+			return;
+                } /* if() else */
+        } /* if() */
+	/* Stick the new entry in cd->trk[]. */
+	if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) *
+						(cur_ntracks + 1))) == NULL)
+		goto nomem;
+
+	if (num)
+		memcpy(newtrk, cd->trk, sizeof(*newtrk) * num);
+	memset(&newtrk[num], 0, sizeof(*newtrk));
+	if (num < cur_ntracks)
+		memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) *
+			(cur_ntracks - num));
+
+	free(cd->trk);
+	cd->trk = newtrk;
+}
+
+/*
+ * split_trackinfo()
+ *
+ * Split a track in two at a particular position (absolute, in frames).  All
+ * internal data structures and variables will be adjusted to the new
+ * numbering scheme.  Pass in the track number (>=1) to split, which is also
+ * the index into cd->trk[] of the new entry.
+ *
+ * If pos is within 1 second of the start of another track, the split fails.
+ *
+ * Returns 1 on success, 0 if the track couldn't be inserted.
+ *
+ * Note: updating user interface elements is up to the caller.
+ */
+int
+split_trackinfo( int pos )
+{
+	int	i, l, num;
+
+	if (pos < cd->trk[0].start)
+		return (0);
+
+	/* First find the appropriate track. */
+	for (num = 0; num < cur_ntracks; num++)
+		if (cd->trk[num].start - 75 < pos &&
+						cd->trk[num].start + 75 > pos)
+			return (0);
+		else if (cd->trk[num].start > pos)
+			break;
+	if (num == 0)
+		return (0);
+
+	/* Insert the new entry into the track array. */
+	insert_trackinfo(num);
+
+	/* Update the easy variables. */
+	if (cur_track > num)
+		cur_track++;
+	if (cur_firsttrack > num)
+		cur_firsttrack++;
+	if (cur_lasttrack > num)
+		cur_lasttrack++;
+
+	/* Update the user-defined playlists. */
+	if (cd->lists != NULL)
+		for (l = 0; cd->lists[l].name != NULL; l++)
+			if (cd->lists[l].list != NULL)
+				for (i = 0; cd->lists[l].list[i]; i++)
+					if (cd->lists[l].list[i] > num)
+						cd->lists[l].list[i]++;
+
+	/* Update the internal playlist. */
+	if (playlist != NULL)
+		for (i = 0; playlist[i].start; i++)
+		{
+			if (playlist[i].start > num)
+				playlist[i].start++;
+			if (playlist[i].end > num)
+				playlist[i].end++;
+		}
+	
+	/* Now adjust the information in cd->trk[]. */
+	cd->trk[num].start = pos;
+	if (num == cur_ntracks)
+		cd->trk[num].length = cur_cdlen - pos / 75;
+	else
+		cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75;
+	cd->trk[num - 1].length -= cd->trk[num].length;
+	if (cur_track == num)
+		cur_tracklen -= cd->trk[num].length;
+	cd->trk[num].track = cd->trk[num - 1].track;
+	cd->trk[num].data = cd->trk[num - 1].data;
+	cd->trk[num].contd = 1;
+	cd->trk[num].volume = cd->trk[num - 1].volume;
+
+	if (cd->trk[num - 1].section == 0)
+		cd->trk[num - 1].section = 1;
+	cd->trk[num].section = cd->trk[num - 1].section + 1;
+
+	cur_ntracks++;
+	cur_nsections++;
+
+	for (i = num + 1; i < cur_ntracks; i++)
+		if (cd->trk[i].track == cd->trk[num].track)
+			cd->trk[i].section++;
+	
+	return (1);
+}
+
+/*
+ * remove_trackinfo()
+ *
+ * Remove a track's internal data.  This is similar to split_trackinfo()
+ * above, but simpler.  A track's initial section can't be removed.  Track
+ * numbers start at 0.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int
+remove_trackinfo( int num )
+{
+	int	i, l;
+
+	if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2)
+		return (0);
+	
+	cd->trk[num - 1].length += cd->trk[num].length;
+
+	for (i = num; i < cur_ntracks - 1; i++)
+		memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0]));
+
+	if (cur_track > num)
+		cur_track--;
+	if (cur_firsttrack > num)
+		cur_firsttrack--;
+	if (cur_lasttrack > num)
+		cur_lasttrack--;
+	
+	/* Update the user-defined playlists. */
+	if (cd->lists != NULL)
+		for (l = 0; cd->lists[l].name != NULL; l++)
+			if (cd->lists[l].list != NULL)
+				for (i = 0; cd->lists[l].list[i]; i++)
+					if (cd->lists[l].list[i] > num)
+						cd->lists[l].list[i]--;
+	
+	/* Update the internal playlist. */
+	if (playlist != NULL)
+		for (i = 0; playlist[i].start; i++)
+		{
+			if (playlist[i].start > num)
+				playlist[i].start--;
+			if (playlist[i].end > num)
+				playlist[i].end--;
+		}
+	
+	cur_ntracks--;
+	cur_nsections--;
+
+	/*
+	 * Update the section numbers for this track.  If this is the only
+	 * user-created section in a track, get rid of the section number
+	 * in the track's entry.
+	 */
+	if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track)
+	{
+		if (cd->trk[num - 1].section == 1)
+			cd->trk[num - 1].section = 0;
+	}
+	else
+		for (i = num; i < cur_ntracks; i++)
+			if (cd->trk[i].track == cd->trk[num - 1].track)
+				cd->trk[i].section--;
+
+	return (1);
+}
+
+/*
+ * listentry()
+ *
+ * Return a scrolling list entry.
+ */
+char *
+listentry( int num )
+{
+	static char	buf[600];
+	char		*name, tracknum[20];
+	int		digits;
+	int		sdigits;
+
+	if (num >= 0 && num < cur_ntracks)
+	{
+
+/*
+		if (big_spaces)
+		{
+			digits = 2;
+			sdigits = cur_nsections < 9 ? -1 : -2;
+		}
+		else
+		{
+			digits = cd->trk[num].track < 10 ? 3 : 2;
+			sdigits = cur_nsections < 9 ? -1 : -3;
+		}
+*/
+
+		digits = 2;
+		sdigits = cur_nsections < 9 ? -1 : -2;
+		
+		name = cd->trk[num].songname ? cd->trk[num].songname : "";
+
+		if (cur_nsections)
+	        {
+    			if (cd->trk[num].section > 9) 
+			{
+				sprintf(tracknum, "%*d.%d", digits,
+					cd->trk[num].track,
+					cd->trk[num].section);
+			} else {
+				if (cd->trk[num].section)
+				{
+					sprintf(tracknum, "%*d.%*d", digits,
+						cd->trk[num].track, sdigits,
+						cd->trk[num].section);
+				} else {
+					sprintf(tracknum, "%*d%*s", digits,
+						cd->trk[num].track,
+						2 - sdigits, " ");
+/*						2 - sdigits - big_spaces, " ");*/
+				}
+			}
+		} else {
+			sprintf(tracknum, "%*d", digits, cd->trk[num].track);
+		}
+
+		if (cd->trk[num].data)
+		{
+			sprintf(buf, "%s) %3dMB %s", tracknum,
+				cd->trk[num].length / 1024, name);
+		} else {
+			sprintf(buf, "%s) %02d:%02d %s", tracknum,
+				cd->trk[num].length / 60,
+				cd->trk[num].length % 60, name);
+                }
+
+		return (buf);
+	} else {
+		return (NULL);
+        }
+} /* listentry() */
+
+/*
+ * trackname()
+ *
+ * Return a track's name.
+ */
+char *
+trackname( int num )
+{
+	if (num >= 0 && num < cur_ntracks) 
+	{
+		if (cd->trk[num].songname)
+		{
+			return (cd->trk[num].songname);
+		} else {
+			return ("");
+		}
+	} else {
+		return (NULL);
+	}
+} /* trackname() */
+
+/*
+ * tracklen()
+ *
+ * Return a track's length in seconds.
+ */
+int
+tracklen( int num )
+{
+	if (cd != NULL && num >= 0 && num < cur_ntracks)
+		return (cd->trk[num].length);
+	else
+		return (0);
+}
+
+/*
+ * get_default_volume()
+ *
+ * Return the default volume (0-32, 0=none) for the CD or a track.
+ */
+int
+get_default_volume( int track )
+{
+	if (! track)
+		return (cd->volume);
+	else if (track <= cur_ntracks)
+		return (cd->trk[track - 1].volume);
+	else
+		return (0);
+}
+
+/*
+ * get_contd()
+ *
+ * Return the contd value for a track.
+ */
+int
+get_contd( int num )
+{
+	if (num >= 0 && num < cur_ntracks)
+		return (cd->trk[num].contd);
+	else
+		return (0);
+}
+
+/*
+ * get_avoid()
+ *
+ * Return the avoid value for a track.
+ */
+int
+get_avoid( int num )
+{
+	if (num >= 0 && num < cur_ntracks)
+		return (cd->trk[num].avoid);
+	else
+		return (0);
+}
+
+/*
+ * get_autoplay()
+ *
+ * Is autoplay set on this disc?
+ */
+int
+get_autoplay( void )
+{
+	return ( cd->autoplay );
+}
+
+/*
+ * get_playmode()
+ *
+ * Return the default playmode for the CD.
+ */
+int
+get_playmode( void )
+{
+	return ( cd->playmode );
+}
+
+/*
+ * get_runtime()
+ *
+ * Return the total running time for the current playlist in seconds.
+ */
+int
+get_runtime( void )
+{
+	int	i;
+
+	if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1)
+		return (cd == NULL ? 0 : cd->length);
+
+	for (i = 0; playlist[i].start; i++)
+		;
+
+	return (playlist[i].starttime);
+}
+
+/*
+ * default_volume()
+ *
+ * Set the default volume for the CD or a track.
+ */
+void
+default_volume( int track, int vol )
+{
+	if (track == 0)
+		cd->volume = vol;
+	else if (track <= cur_ntracks)
+		cd->trk[track - 1].volume = vol;
+}
+
+/*
+ * Play the next thing on the playlist, if any.
+ */
+void
+play_next_entry( int forward )
+{
+	if (cd == NULL)
+		return;
+	if (playlist != NULL && playlist[cur_listno].start)
+	{
+		wm_cd_play(playlist[cur_listno].start, 0,
+			playlist[cur_listno].end);
+		cur_listno++;
+	}
+	else
+		wm_cd_stop();
+}
+
+/*
+ * Play the next track, following playlists as necessary.
+ */
+void
+play_next_track( int forward )
+{
+	if (cd == NULL)
+		return;
+
+	/* Is the current playlist entry done?  Move on, if so. */
+	if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end)
+		play_next_entry( forward );
+	else
+		wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end);
+}
+
+/*
+ * Play the previous track, hopping around the playlist as necessary.
+ */
+void
+play_prev_track( int forward )
+{
+	if (cd == NULL)
+		return;
+
+	if (playlist == NULL)
+		return;
+
+	/* If we're in the middle of this playlist entry, go back one track */
+	if (cur_track > playlist[cur_listno - 1].start)
+		wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end);
+	else
+		if (cur_listno > 1)
+		{
+			cur_listno--;
+			wm_cd_play(playlist[cur_listno - 1].end - 1, 0,
+				playlist[cur_listno - 1].end);
+		}
+		else
+			wm_cd_play(playlist[0].start, 0, playlist[0].end);
+}
+
+/*
+ * stash_cdinfo(artist, cdname)
+ */
+void
+stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode )
+{
+	if (cd != NULL)
+	{
+		if (strcmp(cd->artist, artist))
+			info_modified = 1;
+		strcpy(cd->artist, artist);
+
+		if (strcmp(cd->cdname, cdname))
+			info_modified = 1;
+		strcpy(cd->cdname, cdname);
+
+		if (!!cd->autoplay != !!autoplay)
+			info_modified = 1;
+		cd->autoplay = autoplay;
+
+		if (!!cd->playmode != !!playmode)
+			info_modified = 1;
+		cd->playmode = playmode;
+	}
+} /* stash_cdinfo() */
+
+/*
+ * wipe_cdinfo()
+ *
+ * Clear out all a CD's soft information (presumably in preparation for
+ * reloading from the database.)
+ */
+void
+wipe_cdinfo( void )
+{
+	struct wm_playlist	*l;
+	int		i;
+
+	if (cd != NULL)
+	{
+		cd->artist[0] = cd->cdname[0] = '\0';
+		cd->autoplay = cd->playmode = cd->volume = 0;
+		cd->whichdb = NULL;
+		freeup(&cd->otherrc);
+		freeup(&cd->otherdb);
+
+		if (thiscd.lists != NULL)
+		{
+			for (l = thiscd.lists; l->name != NULL; l++)
+			{
+				free(l->name);
+				free(l->list);
+			}
+			freeup( (char **)&thiscd.lists );
+		}
+
+		for (i = 0; i < cur_ntracks; i++)
+		{
+			freeup(&cd->trk[i].songname);
+			freeup(&cd->trk[i].otherrc);
+			freeup(&cd->trk[i].otherdb);
+			cd->trk[i].avoid = cd->trk[i].contd = 0;
+			cd->trk[i].volume = 0;
+			if (cd->trk[i].section > 1)
+				remove_trackinfo(i--);
+		}
+	}
+}
+
+/*
+ * stash_trkinfo(track, songname, contd, avoid)
+ *
+ * Update information about a track on the current CD.
+ */
+void
+stash_trkinfo( int track, char *songname, int contd, int avoid )
+{
+	if (cd != NULL)
+	{
+		track--;
+		if (!!cd->trk[track].contd != !!contd)
+			info_modified = 1;
+		cd->trk[track].contd = track ? contd : 0;
+
+		if (!!cd->trk[track].avoid != !!avoid)
+			info_modified = 1;
+		cd->trk[track].avoid = avoid;
+
+		if ((cd->trk[track].songname == NULL && songname[0]) ||
+				(cd->trk[track].songname != NULL &&
+				strcmp(cd->trk[track].songname, songname)))
+		{
+			info_modified = 1;
+			wm_strmcpy(&cd->trk[track].songname, songname);
+		}
+	}
+}
+
+/*
+ * new_list()
+ *
+ * Add a playlist to a CD.
+ */
+struct wm_playlist *
+new_list(cd, listname)
+	struct wm_cdinfo	*cd;
+	char		*listname;
+{
+	int	nlists = 0;
+	struct wm_playlist *l;
+
+	if (cd->lists != NULL)
+	{
+		for (nlists = 0; cd->lists[nlists].name != NULL; nlists++)
+			;
+		l = (struct wm_playlist *)realloc(cd->lists, (nlists + 2) *
+			sizeof (struct wm_playlist));
+	}
+	else
+		l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist));
+
+	if (l == NULL)
+		return (NULL);
+
+	l[nlists + 1].name = NULL;
+	l[nlists].name = NULL;		/* so wm_strmcpy doesn't free() it */
+	wm_strmcpy(&l[nlists].name, listname);
+	l[nlists].list = NULL;
+	cd->lists = l;
+
+	return (&l[nlists]);
+}
+
+/*
+ * make_playlist()
+ *
+ * Construct a playlist for the current CD.  If we're in shuffle mode, play
+ * each non-avoided track once, keeping continued tracks in the right order.
+ *
+ * If playmode is 2, use playlist number (playmode-2).  XXX should do
+ * bounds checking on this, probably.
+ *
+ * If consecutive tracks are being played, only make one playlist entry for
+ * them, so the CD player won't pause between tracks while we wake up.
+ */
+void
+make_playlist( int playmode, int starttrack )
+{
+	int	i, avoiding = 1, entry = 0, count, track,
+		*thislist;
+
+	cur_listno = 0;
+	if (playlist != NULL)
+		free(playlist);
+	playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1));
+	if (playlist == NULL)
+	{
+		perror("playlist");
+		exit(1);
+	}
+
+	/* If this is a data-only CD, we can't play it. */
+	if ((starttrack && cd->trk[starttrack - 1].data) ||
+		(cur_ntracks == 1 && cd->trk[0].data))
+	{
+		playlist[0].start = 0;
+		playlist[0].end = 0;
+		playlist[1].start = 0;
+		return;
+	}
+
+	if (playmode == 1)
+	{
+		char *done = malloc(cur_ntracks);
+
+		if (done == NULL)
+		{
+			perror("randomizer");
+			exit(1);
+		}
+
+		count = cur_ntracks;
+		if (starttrack && cd->trk[starttrack - 1].avoid)
+			count++;
+		for (i = 0; i < cur_ntracks; i++)
+			if (cd->trk[i].contd || cd->trk[i].avoid ||
+				cd->trk[i].data)
+			{
+				done[i] = 1;
+				count--;
+			}
+			else
+				done[i] = 0;
+
+		for (i = 0; i < count; i++)
+		{
+			int end;	/* for readability */
+			if (starttrack)
+			{
+				track = starttrack - 1;
+				starttrack = 0;
+			}
+			else
+				while (done[track = rand() % cur_ntracks])
+					;
+
+			playlist[i].start = track + 1;
+
+			/* play all subsequent continuation tracks too */
+			for (end = track + 1; end < cur_ntracks + 1; end++)
+				if (! cd->trk[end].contd ||
+						cd->trk[end].avoid ||
+						cd->trk[end].data)
+					break;
+			playlist[i].end = end + 1;
+
+			done[track]++;
+		}
+		playlist[i].start = 0;
+
+		free(done);
+	}
+	else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name)
+	{
+		count = 2;	/* one terminating entry, and one for start */
+		thislist = cd->lists[playmode - 2].list;
+
+		for (i = 0; thislist[i]; i++)
+			if (thislist[i + 1] != thislist[i] + 1)
+				count++;
+
+		if (playlist != NULL)
+			free(playlist);
+		playlist = malloc(sizeof (*playlist) * count);
+		if (playlist == NULL)
+		{
+			perror("playlist");
+			exit(1);
+		}
+
+		count = 0;
+		if (starttrack)
+		{
+			playlist[0].start = starttrack;
+			for (track = 0; thislist[track]; track++)
+				if (starttrack == thislist[track])
+					break;
+			if (! thislist[track])
+			{
+				playlist[0].end = starttrack + 1;
+				playlist[1].start = thislist[0];
+				count = 1;
+				track = 0;
+			}
+		}
+		else
+		{
+			playlist[0].start = thislist[0];
+			track = 0;
+		}
+
+		for (i = track; thislist[i]; i++)
+			if (thislist[i + 1] != thislist[i] + 1)
+			{
+				playlist[count].end = thislist[i] + 1;
+				count++;
+				playlist[count].start = thislist[i + 1];
+			}
+	}
+	else
+	{
+		for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++)
+			if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data))
+			{
+				playlist[entry].start = i + 1;
+				avoiding = 0;
+			}
+			else if (! avoiding && (cd->trk[i].avoid ||
+						cd->trk[i].data))
+			{
+				playlist[entry].end = i + 1;
+				avoiding = 1;
+				entry++;
+			}
+		if (! avoiding)
+			playlist[entry].end = i + 1;
+		playlist[entry + !avoiding].start = 0;
+	}
+
+	/*
+	 * Now go through the list, whatever its source, and figure out
+	 * cumulative starting times for each entry.
+	 */
+	entry = count = 0;
+	do {
+		playlist[entry].starttime = count;
+
+		if (playlist[entry].start)
+			for (i = playlist[entry].start; i <
+						playlist[entry].end; i++)
+				count += cd->trk[i - 1].length;
+	} while (playlist[entry++].start);
+}
+
+/*
+ * Find a particular track's location in the current playlist.  Sets the
+ * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack).
+ */
+void
+pl_find_track( int track )
+{
+	int	i;
+
+	if (playlist == NULL)
+	{
+		fprintf(stderr, "Null playlist!  Huh?\n");
+		return;
+	}
+
+	for (i = 0; playlist[i].start; i++)
+		if (track >= playlist[i].start && track < playlist[i].end)
+		{
+			cur_listno = i + 1;
+			cur_firsttrack = playlist[i].start;
+			cur_lasttrack = playlist[i].end - 1;
+			return;
+		}
+	
+	/*
+	 * Couldn't find the track in question.  Make a special entry with
+	 * just that track.
+	 */
+	if (! playlist[i].start)
+	{
+		playlist = realloc(playlist, (i + 2) * sizeof(*playlist));
+		if (playlist == NULL)
+		{
+			perror("playlist realloc");
+			exit(1);
+		}
+
+		playlist[i + 1].start = playlist[i + 1].end = 0;
+		playlist[i + 1].starttime = playlist[i].starttime +
+			cd->trk[track - 1].length;
+		playlist[i].start = track;
+		playlist[i].end = track + 1;
+		cur_listno = i + 1;
+		cur_firsttrack = track;
+		cur_lasttrack = track;
+	}
+}
diff --git a/ascd/libworkman/cdrom.c b/ascd/libworkman/cdrom.c
new file mode 100644
index 0000000..bbc0181
--- /dev/null
+++ b/ascd/libworkman/cdrom.c
@@ -0,0 +1,793 @@
+/*
+ * $Id: cdrom.c,v 1.10 1999/05/05 16:34:19 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Interface between most of WorkMan and the low-level CD-ROM library
+ * routines defined in plat_*.c and drv_*.c.  The goal is to have no
+ * platform- or drive-dependent code here.
+ */
+
+static char cdrom_id[] = "$Id: cdrom.c,v 1.10 1999/05/05 16:34:19 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+/* #include <sys/time.h> */
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_cddb.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_database.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdinfo.h"
+
+#ifdef CAN_CLOSE
+#include <fcntl.h>
+#endif
+
+#define WM_MSG_CLASS WM_MSG_CLASS_CDROM
+
+/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */
+/*	toshiba33_proto; <=== Somehow, this got lost */
+
+/*
+ * The supported drive types are listed here.  NULL means match anything.
+ * The first match in the list is used, and substring matches are done (so
+ * put long names before their shorter prefixes.)
+ */
+struct drivelist {
+	char		*ven;
+	char		*mod;
+	char		*rev;
+	struct wm_drive	*proto;
+} drives[] = {
+{	"TOSHIBA",		"XM-3501",		NULL,		&toshiba_proto	},
+{	"TOSHIBA",		"XM-3401",		NULL,		&toshiba_proto	},
+{	"TOSHIBA",		"XM-3301",		NULL,		&toshiba_proto	},
+{	"SONY",			"CDU-8012",		NULL,		&sony_proto	},
+{	"SONY",			"CDU 561",		NULL,		&sony_proto	},
+{	WM_STR_GENVENDOR,	WM_STR_GENMODEL,	WM_STR_GENREV,	&generic_proto  },
+{	NULL,			NULL,			NULL,		&generic_proto	}
+};
+
+/*
+ * Solaris 2.2 will remove the device out from under us.  Getting an ENOENT
+ * is therefore sometimes not a problem.
+ */
+int	intermittent_dev = 0;
+
+/*
+ * Do we want to keep the CD device open after quitting by default?
+ *
+   int	keep_open = 0;
+ */
+
+#if defined DEFAULT_CD_DEVICE
+char	*cd_device = DEFAULT_CD_DEVICE;
+#else
+char	*cd_device = NULL;
+#endif
+
+
+int wm_cd_cur_balance = 10;
+
+struct wm_drive	drive = { -1, "", "", "", NULL, NULL };
+
+char _wm_drive_vendor[32] = 	"Generic";
+char _wm_drive_model[32] = 	"drive type";
+char _wm_drive_revision[32] =	"";
+
+/*
+ * Give information about the drive we found during wmcd_open()
+ */
+
+char *wm_drive_vendor( void )
+{
+	char *s = NULL;
+
+	wm_strmcpy( &s, _wm_drive_vendor );
+	return s;
+}
+
+char *wm_drive_model( void )
+{
+	char *s = NULL;
+
+	wm_strmcpy( &s, _wm_drive_model );
+	return s;
+}
+
+char *wm_drive_revision( void )
+{
+	char *s = NULL;
+
+	wm_strmcpy( &s, _wm_drive_revision );
+	return s;
+}
+
+void wm_drive_settype( char *vendor, char *model, char *revision )
+{
+	sprintf( _wm_drive_vendor, "%s", vendor );
+	sprintf( _wm_drive_model, "%s", model );
+	sprintf( _wm_drive_revision, "%s", revision );
+}
+
+/*
+ * Figure out which prototype drive structure we should be using based
+ * on the vendor, model, and revision of the current drive.
+ */
+struct wm_drive *
+find_drive_struct(char *vendor, char *model, char *rev)
+{
+	struct drivelist	*d;
+
+	for (d = drives; d; d++)
+	{
+		if( ( (d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven)) ) ||
+		    ( (d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod)) ) ||
+		    ( (d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev)) ) )
+			continue;
+		
+		if (d->proto->vendor[0] == '\0')
+			strcpy(d->proto->vendor, vendor);
+		if (d->proto->model[0] == '\0')
+			strcpy(d->proto->model, model);
+
+		return (d->proto);
+	}
+
+	return (NULL);	/* this means the list is badly terminated. */
+} /* find_drive_struct() */
+
+/*
+ * read_toc()
+ *
+ * Read the table of contents from the CD.  Return a pointer to a wm_cdinfo
+ * struct containing the relevant information (minus artist/cdname/etc.)
+ * This is a static struct.  Returns NULL if there was an error.
+ *
+ * XXX allocates one trackinfo too many.
+ */
+struct wm_cdinfo *
+read_toc()
+{
+	struct wm_playlist		*l;
+	int			i, pos;
+
+	if ((drive.get_trackcount)(&drive, &thiscd.ntracks) < 0)
+	{
+		perror("trackcount");
+		return (NULL);
+	}
+
+	thiscd.artist[0] = thiscd.cdname[0] = '\0';
+	thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL;
+	thiscd.length = 0;
+	thiscd.autoplay = thiscd.playmode = thiscd.volume = 0;
+
+	/* Free up any left-over playlists. */
+	if (thiscd.lists != NULL)
+	{
+		for (l = thiscd.lists; l->name != NULL; l++)
+		{
+			free(l->name);
+			free(l->list);
+		}
+		free(thiscd.lists);
+		thiscd.lists = NULL;
+	}
+
+	if (thiscd.trk != NULL)
+		free(thiscd.trk);
+
+	thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo));
+	if (thiscd.trk == NULL)
+	{
+		perror("malloc");
+		return (NULL);
+	}
+
+	for (i = 0; i < thiscd.ntracks; i++)
+	{
+		if ((drive.get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data,
+					&thiscd.trk[i].start) < 0)
+		{
+			perror("CD track info read");
+			return (NULL);
+		}
+
+		thiscd.trk[i].avoid = thiscd.trk[i].data;
+		thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+		thiscd.trk[i].songname = thiscd.trk[i].otherrc =
+		thiscd.trk[i].otherdb = NULL;
+		thiscd.trk[i].contd = 0;
+		thiscd.trk[i].volume = 0;
+		thiscd.trk[i].track = i + 1;
+		thiscd.trk[i].section = 0;
+	}
+
+	if ((drive.get_cdlen)(&drive, &thiscd.trk[i].start) < 0)
+	{
+		perror("CD length read");
+		return (NULL);
+	}
+	thiscd.trk[i].length = thiscd.trk[i].start / 75;
+
+/* Now compute actual track lengths. */
+	pos = thiscd.trk[0].length;
+
+	for (i = 0; i < thiscd.ntracks; i++)
+	{
+		thiscd.trk[i].length = thiscd.trk[i+1].length - pos;
+		pos = thiscd.trk[i+1].length;
+		if (thiscd.trk[i].data)
+			thiscd.trk[i].length = (thiscd.trk[i + 1].start -
+				thiscd.trk[i].start) * 2;
+		if (thiscd.trk[i].avoid)
+			wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK");
+	}
+
+	thiscd.length = thiscd.trk[thiscd.ntracks].length;
+        thiscd.cddbid = cddb_discid(drive);
+        
+	return (&thiscd);
+}
+
+/*
+ * wm_cd_status()
+ *
+ * Return values:
+ *
+ *	0	No CD in drive.
+ *	1	CD in drive.
+ *	2	CD has just been inserted (TOC has been read)
+ *
+ * Updates cur_track, cur_pos_rel, cur_pos_abs and other variables.
+ */
+int
+wm_cd_status( void )
+{
+	static enum wm_cd_modes	oldmode = WM_CDM_UNKNOWN;
+	enum wm_cd_modes		mode;
+	int			status, trackno = cur_track;
+	int			ret = WM_CDS_DISC_READY;
+
+	/* Open the drive.  This returns 1 if the device isn't ready. */
+
+	status = wmcd_open(&drive);
+
+	if (status < 0)
+		return (status);
+	if (status > 0)
+		return (WM_CDS_NO_DISC);
+
+	/* If the user hit the stop button, don't pass PLAYING as oldmode.
+         * Likewise, if we've just started playing, don't remember that
+         * we were stopped before (or the state machine in get_drive_status
+         * can get confused.)
+         */
+	if( (cur_cdmode == WM_CDM_STOPPED) || (cur_cdmode == WM_CDM_PLAYING) )
+		oldmode = cur_cdmode;
+
+	if( (drive.get_drive_status)(&drive, oldmode, &mode, &cur_frame,
+					&trackno, &cur_index) < 0)
+	{
+		perror("CD get drive status");
+		return (-1);
+	}
+	oldmode = mode;
+
+	if (mode == WM_CDM_EJECTED || mode == WM_CDM_UNKNOWN)
+	{
+		cur_cdmode = WM_CDM_EJECTED;
+		cur_track = -1;
+		cur_cdlen = cur_tracklen = 1;
+		cur_pos_abs = cur_pos_rel = cur_frame = 0;
+
+		if (exit_on_eject)
+			exit(0);
+
+		return (WM_CDS_NO_DISC);
+	}
+
+	/* If there wasn't a CD before and there is now, learn about it. */
+	if (cur_cdmode == WM_CDM_EJECTED)
+	{
+		cur_pos_rel = cur_pos_abs = 0;
+
+
+		status = wmcd_reopen( &drive );
+
+		if ((cd = read_toc()) == NULL)
+                {
+			if (exit_on_eject)
+			{
+				exit(-1);
+			} else {
+				return (-1);
+			}
+		}
+
+		cur_nsections = 0;
+		cur_ntracks = cd->ntracks;
+		cur_cdlen = cd->length;
+		load();
+		cur_artist = cd->artist;
+		cur_cdname = cd->cdname;
+		cur_cdmode = WM_CDM_STOPPED;
+		ret = WM_CDS_JUST_INSERTED;
+	}
+
+	switch (mode) {
+	case WM_CDM_PLAYING:
+	case WM_CDM_PAUSED:
+		cur_pos_abs = cur_frame / 75;
+
+		/* Only look up the current track number if necessary. */
+		if (cur_track < 1 || cur_frame < cd->trk[cur_track-1].start ||
+				cur_frame >= (cur_track >= cur_ntracks ?
+				(cur_cdlen + 1) * 75 :
+				cd->trk[cur_track].start))
+		{
+			cur_track = 0;
+			while (cur_track < cur_ntracks && cur_frame >=
+					cd->trk[cur_track].start)
+				cur_track++;
+		}
+		if (cur_track >= 1 && trackno > cd->trk[cur_track-1].track)
+			cur_track++;
+		/* Fall through */
+
+	case WM_CDM_UNKNOWN:
+		if (mode == WM_CDM_UNKNOWN)
+		{
+			mode = WM_CDM_STOPPED;
+			cur_lasttrack = cur_firsttrack = -1;
+		}
+		/* Fall through */
+
+	case WM_CDM_STOPPED:
+		if (cur_track >= 1 && cur_track <= cur_ntracks)
+		{
+			cur_trackname = cd->trk[cur_track-1].songname;
+			cur_avoid = cd->trk[cur_track-1].avoid;
+			cur_contd = cd->trk[cur_track-1].contd;
+			cur_pos_rel = (cur_frame -
+				cd->trk[cur_track-1].start) / 75;
+			if (cur_pos_rel < 0)
+				cur_pos_rel = -cur_pos_rel;
+		}
+
+		if( (playlist != NULL) && playlist[0].start & (cur_listno > 0))
+		{
+			cur_pos_abs -= cd->trk[playlist[cur_listno-1].
+				start - 1].start / 75;
+			cur_pos_abs += playlist[cur_listno-1].starttime;
+		}
+		if (cur_pos_abs < 0)
+			cur_pos_abs = cur_frame = 0;
+
+		if (cur_track < 1)
+			cur_tracklen = cd->length;
+		else
+			cur_tracklen = cd->trk[cur_track-1].length;
+		/* Fall through */
+
+	case WM_CDM_TRACK_DONE:
+		cur_cdmode = mode;
+		break;
+	case WM_CDM_FORWARD:
+	case WM_CDM_EJECTED:
+		break;	
+	}
+
+	return (ret);
+}
+
+#undef CLIF_VOL 
+#ifdef CLIF_VOL
+/*
+ * cd_volume(vol, bal, max)
+ *
+ * Set the volume levels.  "vol" and "bal" are the volume and balance knob
+ * settings, respectively.  "max" is the maximum value of the volume knob
+ * (the balance knob is assumed to always go from 0 to 20.)
+ */
+void
+cd_volume(vol, bal, max)
+	int	vol, bal, max;
+{
+	int	left, right, scale;
+
+/*
+ * Set "left" and "right" to volume-slider values accounting for the
+ * balance setting.
+ */
+/*	printf("Vol = %d, Bal = %d, Max = %d\n", vol, bal, max);
+*/
+
+        vol  = (vol * 100 + max - 16) / max;  
+	scale = (vol + 5) / 10;
+
+        if (bal < 9)
+	{
+                right = vol - scale * (10 - bal);
+#ifdef SYMETRIC_BALANCE
+		left  = vol + scale * (10 - bal);
+#else
+		left = vol;
+#endif
+        }
+	else if (bal > 11)
+	{
+#ifdef SYMETRIC_BALANCE		
+                right = vol + scale * (bal - 10);
+#else
+		right = vol;
+#endif
+		left  = vol - scale * (bal - 10);
+        }
+	else
+                left = right = vol;
+
+/*
+ * some plat_*.c is missing the limitation
+ */
+	left = left < 0 ? 0 : left > 100 ? 100 : left;
+	right = right < 0 ? 0 : right > 100 ? 100 : right;
+/*	printf("Left = %d, Right = %d\n", left, right);
+*/
+	(void) (drive.set_volume)(&drive, left, right);
+} /* cd_volume() */
+
+#else
+
+/*
+ * cd_volume(vol, bal, max)
+ *
+ * Set the volume levels.  "vol" and "bal" are the volume and balance knob
+ * settings, respectively.  "max" is the maximum value of the volume knob
+ * (the balance knob is assumed to always go from 0 to 20.)
+ */
+void
+cd_volume( int vol, int bal, int max )
+{
+	int	left, right;
+
+/*
+ * Set "left" and "right" to volume-slider values accounting for the
+ * balance setting.
+ *
+ * XXX - the maximum volume setting is assumed to be in the 20-30 range.
+ */
+	if (bal < 9)
+		right = vol - (9 - bal) * 2;
+	else
+		right = vol;
+	if (bal > 11)
+		left = vol - (bal - 11) * 2;
+	else
+		left = vol;
+
+	left = (left * 100 + max - 1) / max;
+	right = (right * 100 + max - 1) / max;
+	if (left > 100)
+		left = 100;
+	if (right > 100)
+		right = 100;
+
+	(void) (drive.set_volume)(&drive, left, right);
+} /* cd_volume() */
+
+#endif /* CLIF_VOL */
+
+
+/*
+ * wm_cd_pause()
+ *
+ * Pause the CD, if it's in play mode.  If it's already paused, go back to
+ * play mode.
+ */
+void
+wm_cd_pause( void )
+{
+	static int paused_pos;
+
+	if (cur_cdmode == WM_CDM_EJECTED)	/* do nothing if there's no CD! */
+		return;
+
+	switch (cur_cdmode) {
+	case WM_CDM_PLAYING:		/* playing */
+		cur_cdmode = WM_CDM_PAUSED;
+		(drive.pause)(&drive);
+                paused_pos = cur_pos_rel;
+		break;
+
+	case WM_CDM_PAUSED:		/* paused */
+		cur_cdmode = WM_CDM_PLAYING;
+/*		(drive.resume)(&drive); */
+		if ((drive.resume)(&drive))
+			wm_cd_play(cur_track, paused_pos,
+				playlist[cur_listno-1].end);
+	default: /* */
+		break;	
+	}
+} /* wm_cd_pause() */
+
+/*
+ * wm_cd_stop()
+ *
+ * Stop the CD if it's not already stopped.
+ */
+void
+wm_cd_stop( void )
+{
+	if (cur_cdmode == WM_CDM_EJECTED)
+		return;
+
+	if (cur_cdmode != WM_CDM_STOPPED)
+	{
+		cur_lasttrack = cur_firsttrack = -1;
+		cur_cdmode = WM_CDM_STOPPED;
+		(drive.stop)(&drive);
+		cur_track = 1;
+	}
+} /* wm_cd_stop() */
+ 
+/*
+ * wm_cd_play_chunk(start, end)
+ *
+ * Play the CD from one position to another (both in frames.)
+ */
+void
+wm_cd_play_chunk( int start, int end, int realstart )
+{
+	if (cur_cdmode == WM_CDM_EJECTED || cd == NULL)
+		return;
+
+	end--;
+	if (start >= end)
+		start = end-1;
+
+	(drive.play)(&drive, start, end, realstart);
+}
+
+/*
+ * wm_cd_play(starttrack, pos, endtrack)
+ *
+ * Start playing the CD or jump to a new position.  "pos" is in seconds,
+ * relative to start of track.
+ */
+void
+wm_cd_play( int start, int pos, int end )
+{
+	if (cur_cdmode == WM_CDM_EJECTED || cd == NULL)
+		return;
+
+	cur_firsttrack = start;
+	start--;
+	end--;
+	cur_lasttrack = end;
+
+	wm_cd_play_chunk(cd->trk[start].start + pos * 75, end >= cur_ntracks ?
+		cur_cdlen * 75 : cd->trk[end].start - 1,
+		cd->trk[start].start);
+
+	/* So we don't update the display with the old frame number */
+	wm_cd_status();
+	cur_frame = cd->trk[start].start + pos * 75;
+	cur_track = cur_firsttrack;
+	cur_cdmode = WM_CDM_PLAYING;
+}
+
+/*
+ * Set the offset into the current track and play.  -1 means end of track
+ * (i.e., go to next track.)
+ */
+void
+wm_cd_play_from_pos( int pos )
+{
+	if (pos == -1)
+		if (cd)
+			pos = cd->trk[cur_track - 1].length - 1;
+	if (cur_cdmode == WM_CDM_PLAYING)
+		wm_cd_play(cur_track, pos, playlist[cur_listno-1].end);
+} /* wm_cd_play_from_pos() */
+
+/*
+ * Eject the current CD, if there is one, and set the mode to 5.
+ *
+ * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the
+ * CD contains a mounted filesystem.
+ */
+int
+wm_cd_eject( void )
+{
+	int	status;
+
+	status = (drive.eject)(&drive);
+	if (status < 0)
+	{
+		if (status == -3)
+		{
+			return (2);
+		} else {
+			return (1);
+		}
+	}
+	
+	if (exit_on_eject)
+		exit(0);
+
+	cur_track = -1;
+	cur_cdlen = cur_tracklen = 1;
+	cur_pos_abs = cur_pos_rel = cur_frame = 0;
+	cur_cdmode = WM_CDM_EJECTED;
+
+	return (0);
+}
+
+int wm_cd_closetray(void)
+{
+	return((drive.closetray)(&drive) ? 0 : wm_cd_status()==2 ? 1 : 0);
+} /* wm_cd_closetray() */
+
+/*
+ * find_trkind(track, index, start)
+ *
+ * Start playing at a particular track and index, optionally using a particular
+ * frame as a starting position.  Returns a frame number near the start of the
+ * index mark if successful, 0 if the track/index didn't exist.
+ *
+ * This is made significantly more tedious (though probably easier to port)
+ * by the fact that CDROMPLAYTRKIND doesn't work as advertised.  The routine
+ * does a binary search of the track, terminating when the interval gets to
+ * around 10 frames or when the next track is encountered, at which point
+ * it's a fair bet the index in question doesn't exist.
+ */
+int
+find_trkind( int track, int index, int start )
+{
+	int	top = 0, bottom, current, interval, ret = 0, i;
+
+	if( cur_cdmode == WM_CDM_EJECTED || cd == NULL )
+		return ( 0 ); /* WARNING: was nothing */
+
+	for (i = 0; i < cur_ntracks; i++)
+		if (cd->trk[i].track == track)
+			break;
+	bottom = cd->trk[i].start;
+
+	for (; i < cur_ntracks; i++)
+		if (cd->trk[i].track > track)
+			break;
+
+	top = i == cur_ntracks ? (cd->length - 1) * 75 : cd->trk[i].start;
+
+	if (start > bottom && start < top)
+		bottom = start;
+
+	current = (top + bottom) / 2;
+	interval = (top - bottom) / 4;
+
+	do {
+		wm_cd_play_chunk(current, current + 75, current);
+
+		if (wm_cd_status() != 1)
+			return (0);
+		while (cur_frame < current)
+			if (wm_cd_status() != 1 || cur_cdmode != WM_CDM_PLAYING)
+				return (0);
+			else
+				wm_susleep(1);
+
+		if (cd->trk[cur_track - 1].track > track)
+			break;
+
+		if (cur_index >= index)
+		{
+			ret = current;
+			current -= interval;
+		}
+		else
+			current += interval;
+		interval /= 2;
+	} while (interval > 2);
+
+	return (ret);
+} /* find_trkind() */
+
+/*
+ * Read the initial volume from the drive, if available.  Set cur_balance to
+ * the balance level (0-20, 10=centered) and return the proper setting for
+ * the volume knob.
+ *
+ * "max" is the maximum value of the volume knob.
+ */
+int
+wm_cd_read_initial_volume( int max )
+{
+	int	left, right;
+
+	if ((drive.get_volume)(&drive, &left, &right) < 0 || left == -1)
+		return (max);
+
+	left = (left * max + 99) / 100;
+	right = (right * max + 99) / 100;
+
+	if (left < right)
+	{
+		wm_cd_cur_balance = (right - left) / 2 + 11;
+		if (wm_cd_cur_balance > 20)
+			wm_cd_cur_balance = 20;
+
+		return (right);
+	}
+	else if (left == right)
+	{
+		wm_cd_cur_balance = 10;
+		return (left);
+	}
+	else
+	{
+		wm_cd_cur_balance = (right - left) / 2 + 9;
+		if (wm_cd_cur_balance < 0)
+			wm_cd_cur_balance = 0;
+
+		return (left);
+	}
+} /* wm_cd_read_initial_volume() */
+
+/*
+ * Prototype wm_drive structure, with generic functions.  The generic functions
+ * will be replaced with drive-specific functions as appropriate once the drive
+ * type has been sensed.
+ */
+struct wm_drive generic_proto = {
+	-1,			/* fd */
+	"Generic\0",		/* vendor */
+	"drive type\0     ",	/* model */
+	"\0",			/* revision */
+	NULL,			/* aux */
+	NULL,			/* daux */
+
+	gen_init,		/* functions... */
+	gen_get_trackcount,
+	gen_get_cdlen,
+	gen_get_trackinfo,
+	gen_get_drive_status,
+	gen_get_volume,
+	gen_set_volume,
+	gen_pause,
+	gen_resume,
+	gen_stop,
+	gen_play,
+	gen_eject,
+	gen_closetray
+};
diff --git a/ascd/libworkman/database.c b/ascd/libworkman/database.c
new file mode 100644
index 0000000..6bb50b8
--- /dev/null
+++ b/ascd/libworkman/database.c
@@ -0,0 +1,1522 @@
+/*
+ * $Id: database.c,v 1.8 1999/05/28 03:35:54 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Manage the CD database.  All these routines assume that the "cd" global
+ * structure contains current information (as far as the outside world knows;
+ * obviously it won't contain track titles just after a CD is inserted.)
+ */
+
+static char database_id[] = "$Id: database.c,v 1.8 1999/05/28 03:35:54 dirk Exp $";
+
+#define RCFILE "/.workmanrc"
+#define DBFILE "/.workmandb"
+#define FUZZFRAMES 75
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+#include "include/wm_cdinfo.h"
+#include "include/wm_cddb.h"
+#include "include/wm_index.h"
+#include "include/wm_database.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_DB
+
+#define SWALLOW_LINE(fp) { int c; while ((c = getc(fp)) != '\n' && c != EOF); }
+
+int	suppress_locking = 0;	/* Turn off locking of datafile (dangerous) */
+
+char	*rcfile = NULL;		/* Personal rcfile */
+char	*dbfiles = NULL;	/* Colon-separated list of databases */
+char	**databases = NULL;	/* NULL-terminated list of databases */
+
+char	*otherrc = NULL;	/* Unrecognized cruft from start of rcfile */
+
+long	rcpos, rclen;		/* XXX */
+
+int	found_in_db, found_in_rc;
+long	holepos, firstpos;
+
+int fuzz_frames = FUZZFRAMES;
+
+int wm_db_save_disabled = FALSE;
+
+int cur_playnew = -1;
+
+extern int cur_ntracks, cur_nsections;
+
+int mark_a = 0;
+int mark_b = 0;
+
+
+/*
+ *
+ */
+int wm_db_get_playnew( void )
+{
+	return 0;
+}
+
+/*
+ * split_workmandb()
+ *
+ * Split the WORKMANDB environment variable, if any, into a list of database
+ * files in the global "databases".  If WORKMANDB is not available, make
+ * a single entry with $HOME/DBFILE.
+ *
+ * Also, fill the "rcfile" global with the personal preferences filename.
+ *
+ * The environment variables should have already been read and placed in the
+ * "rcfile" and "dbfiles" globals, respectively.
+ */
+void
+split_workmandb( void )
+{
+	int	ndbs, i;
+	char	*home, *wmdb;
+	int	no_rc = 0, no_db = 0;
+	
+	if (rcfile == NULL)
+	{
+		if ((home = getenv("HOME")) != NULL)
+		{
+			rcfile = malloc(strlen(home) + sizeof(RCFILE));
+			if (rcfile == NULL)
+			{
+			
+nomem:
+				perror("split_workmandb()");
+				exit(1);
+			}
+
+			strcpy(rcfile, home);
+			strcat(rcfile, RCFILE);
+		}
+		else
+			no_rc = 1;
+
+	}
+
+	if ((wmdb = dbfiles) == NULL)
+	{
+		if ((home = getenv("HOME")) != NULL)
+		{
+			wmdb = malloc(strlen(home) + sizeof(DBFILE));
+			if (wmdb == NULL)
+				goto nomem;
+
+			databases = malloc(2 * sizeof (databases[0]));
+			if (databases == NULL)
+				goto nomem;
+
+			strcpy(wmdb, home);
+			strcat(wmdb, DBFILE);
+			databases[0] = wmdb;
+			databases[1] = NULL;
+		}
+		else
+		{
+			static char *emptydb = NULL;
+		
+			databases = &emptydb;
+			no_db = 1;
+		}
+	}
+	else
+	{
+		ndbs = 1;
+		for (home = wmdb; *home; home++)
+			if (*home == ':')
+			{
+				*home = '\0';
+				ndbs++;
+			}
+		
+		databases = malloc((ndbs + 1) * sizeof(databases[0]));
+		if (databases == NULL)
+			goto nomem;
+		
+		for (i = 0; i < ndbs; i++)
+		{
+			databases[i] = wmdb;
+			wmdb += strlen(wmdb) + 1;
+		}
+
+		databases[i] = NULL;
+	}
+
+	if (no_db || no_rc)
+	{
+		fprintf(stderr,
+"WorkMan was run without a home directory, probably by a system daemon.\n");
+		fprintf(stderr, "It doesn't know where to find ");
+		if (no_rc)
+		{
+			fprintf(stderr, "your personal preferences file ");
+			if (no_db)
+				fprintf(stderr, "or the\ndatabase of CD descriptions");
+		}
+		else
+			fprintf(stderr, "the database of CD descriptions");
+
+		fprintf(stderr,
+".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n");
+
+		wm_db_save_disabled = TRUE;
+	}
+}
+
+/*
+ * print_cdinfo(cd, prefs)
+ *
+ * cd		A pointer to a cdinfo struct.
+ * prefs	Flag: write personal preferences?
+ *
+ * Print a CD's information (in more or less readable form) to a buffer.
+ * Returns a pointer to the buffer.
+ *
+ * XXX - could be more efficient about calling wm_strmcat() and strlen().
+ */
+char *
+print_cdinfo(struct wm_cdinfo *cd, int prefs)
+{
+	int		i;
+	char		tempbuf[2000];	/* XXX - is this always big enough? */
+	static char	*cdibuf = NULL;
+	struct wm_playlist	*l;
+
+	sprintf(tempbuf, "\ntracks %d", cd->ntracks);
+	for (i = 0; i < cur_ntracks; i++)
+		if (cd->trk[i].section < 2)
+			sprintf(tempbuf + strlen(tempbuf), " %d",
+				cd->trk[i].start);
+	sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length);
+
+	wm_strmcpy(&cdibuf, tempbuf);
+
+	if (cur_nsections)
+	{
+		sprintf(tempbuf, "sections %d", cur_nsections);
+		/* fixed a bug here */
+		for (i = 0; i < cur_ntracks; i++)
+			if (cd->trk[i].section > 1)
+				sprintf(tempbuf + strlen(tempbuf), " %d",
+					cd->trk[i].start);
+		sprintf(tempbuf + strlen(tempbuf), "\n");
+
+		wm_strmcat(&cdibuf, tempbuf);
+	}
+
+	if (prefs)
+	{
+		if (cd->autoplay)
+			wm_strmcat(&cdibuf, "autoplay\n");
+		for (l = cd->lists; l != NULL && l->name != NULL; l++)
+		{
+			wm_strmcat(&cdibuf, "playlist ");
+
+			i = strlen(cdibuf) - 1;
+			wm_strmcat(&cdibuf, l->name);
+			while (cdibuf[++i])
+				if (cdibuf[i] == ' ' || cdibuf[i] == '\t')
+					cdibuf[i] = '_';
+
+			if (l->list != NULL)
+			{
+				for (i = 0; l->list[i]; i++)
+					;
+				sprintf(tempbuf, " %d", i);
+				wm_strmcat(&cdibuf, tempbuf);
+				for (i = 0; l->list[i]; i++)
+				{
+					sprintf(tempbuf, " %d", l->list[i]);
+					wm_strmcat(&cdibuf, tempbuf);
+				}
+				wm_strmcat(&cdibuf, "\n");
+			}
+			else
+				wm_strmcat(&cdibuf, " 0\n");
+		}
+
+		if (cd->volume)
+		{
+			/*
+			 * Have to maintain compatibility with old versions,
+			 * where volume was 0-32.
+			 */
+			sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100);
+			wm_strmcat(&cdibuf, tempbuf);
+		}
+
+		if (cd->playmode)
+		{
+			sprintf(tempbuf, "playmode %d\n", cd->playmode);
+			wm_strmcat(&cdibuf, tempbuf);
+		}
+
+		if (mark_a)
+		{
+			sprintf(tempbuf, "mark %d START\n", mark_a);
+			wm_strmcat(&cdibuf, tempbuf);
+		}
+		if (mark_b)
+		{
+			sprintf(tempbuf, "mark %d END\n", mark_b);
+			wm_strmcat(&cdibuf, tempbuf);
+		}
+
+		if (cd->otherrc)
+			wm_strmcat(&cdibuf, cd->otherrc);
+
+		for (i = 0; i < cur_ntracks; i++)
+		{
+			if (cd->trk[i].avoid)
+			{
+				sprintf(tempbuf, "dontplay %d\n", i + 1);
+				wm_strmcat(&cdibuf, tempbuf);
+			}
+			if (cd->trk[i].volume)
+			{
+				sprintf(tempbuf, "volume %d %d\n", i + 1,
+					(cd->trk[i].volume * 32) / 100);
+				wm_strmcat(&cdibuf, tempbuf);
+			}
+			if (cd->trk[i].otherrc)
+				wm_strmcat(&cdibuf, cd->trk[i].otherrc);
+		}
+	}
+	else
+	{
+		if (cd->cdname[0])
+		{
+			wm_strmcat(&cdibuf, "cdname ");
+			wm_strmcat(&cdibuf, cd->cdname);
+			wm_strmcat(&cdibuf, "\n");
+		}
+
+		if (cd->artist[0])
+		{
+			wm_strmcat(&cdibuf, "artist ");
+			wm_strmcat(&cdibuf, cd->artist);
+			wm_strmcat(&cdibuf, "\n");
+		}
+
+		if (cd->otherdb)
+			wm_strmcat(&cdibuf, cd->otherdb);
+
+		for (i = 0; i < cur_ntracks; i++)
+		{
+			if (cd->trk[i].section > 1)
+				wm_strmcat(&cdibuf, "s-");
+			wm_strmcat(&cdibuf, "track ");
+			if (cd->trk[i].songname != NULL)
+				wm_strmcat(&cdibuf, cd->trk[i].songname);
+			wm_strmcat(&cdibuf, "\n");
+			if (cd->trk[i].contd)
+			{
+				if (cd->trk[i].section > 1)
+					wm_strmcat(&cdibuf, "s-");
+				wm_strmcat(&cdibuf, "continue\n");
+			}
+			if (cd->trk[i].otherdb)
+				wm_strmcat(&cdibuf, cd->trk[i].otherdb);
+		}
+	}
+
+	return (cdibuf);
+} /* print_cdinfo() */
+
+/*
+ * Open the rcfile for reading or writing.
+ *
+ *	name		Filename
+ *	mode		"r" or "w"
+ */
+FILE *
+open_rcfile(char *name, char *mode)
+{
+	FILE		*fp;
+	struct stat	st;
+
+	fp = fopen(name, mode);
+	if (fp == NULL)
+	{
+		if (errno != ENOENT || mode[0] == 'w')
+			perror(name);
+	}
+	else
+	{
+		/* Don't let people open directories or devices */
+		if (fstat(fileno(fp), &st) < 0)
+		{
+			perror(name);
+			fclose(fp);
+			return (NULL);
+		}
+
+#ifdef S_ISREG
+		if (! S_ISREG(st.st_mode))
+#else
+		if ((st.st_mode & S_IFMT) != S_IFREG)
+#endif
+		{
+			errno = EISDIR;
+			perror(name);
+			fclose(fp);
+			return (NULL);
+		}
+
+		if (mode[0] == 'w') /* create -- put data in so locks work */
+		{
+			fputs("# WorkMan database file\n", fp);
+			fclose(fp);
+			fp = fopen(name, "r+");
+			if (fp == NULL)
+				if (errno != ENOENT)
+					perror(name);
+		}
+	}
+
+	return (fp);
+}
+
+/*
+ *
+ * allocate and clear "trackmap".
+ *
+ */
+int *reset_tracks(void)
+{
+    int i, j;
+    int *trackmap;
+    /*
+     * Since we access track numbers indirectly (to handle sections
+     * with at least a little elegance), the track mapping needs to be
+     * set up before we read anything.  Initially it must assume that
+     * no sections will appear in this datafile.
+     */
+    trackmap = malloc(sizeof(int) * cur_ntracks);
+    if (trackmap == NULL)
+    {
+    	perror("trackmap");
+	exit(1);
+    }
+    j = 0;
+    for (i = 0; i < cd->ntracks; i++)
+    {
+	trackmap[i] = j;
+	while (cd->trk[++j].section > 1)
+		;
+    }
+    return trackmap;
+} /* reset_tracks() */
+
+/*
+ * Load a new-format database file, searching for a match with the currently
+ * inserted CD.  Modify the in-core copy of the CD info based on what's found
+ * in the database.
+ *
+ * Returns 1 if there was a match or 0 if not.
+ *
+ *	fp		FILE* of database or rcfile.
+ *	prefs		1 if we're searching .workmanrc, 0 for .workmandb,
+ *			2 if just reading settings
+ *	scan		Scan for "tracks" location and entry size only
+ *	holesize_wanted	How big a hole we're looking for, if any.
+ *
+ * If a hole was found along the way, update the global "holepos" with its
+ * starting offset in the file.  A hole is defined as a bunch of blank lines
+ * preceding a "tracks" line.  Holepos will contain the best match.
+ *
+ * In addition, "firstpos" will be filled with the position of the first
+ * "tracks" keyword, so we know how much room is available for global
+ * settings at the rcfile's start.
+ */
+int
+search_db( FILE *fp, int prefs, int scan, int holesize_wanted )
+
+{
+	char	keyword[64], listname[64], *c;
+	int	b, i, j, track = 0, listsize, ntracks, scratch, searching = 1;
+	int	*trackmap = 0, gotsections = 0;
+        int     fudge, maxfudge, sizediff, bestfudge = 0;
+	long	pos = 0, thisholepos = -1, holesize = 99991239;
+	struct	wm_playlist *l;
+
+	rclen = 0;
+
+	/* We may not find any holes at all! */
+	if (holesize_wanted)
+		holepos = -1;
+
+	if( prefs != 2 )
+		trackmap = reset_tracks();
+
+	if (prefs)
+		freeup(&otherrc);
+	firstpos = -1;
+	while (! feof(fp))
+	{
+		pos = ftell(fp);
+		keyword[0] = '\0';
+		do
+			b = getc(fp);
+		while (b != EOF && b != '\n' && isspace(b));
+
+		if (b == EOF || feof(fp))
+			break;
+
+		if (b != '\n')
+		{
+			keyword[0] = b;
+			fscanf(fp, "%s", &keyword[1]);
+		}
+		if (keyword[0] == '\0')		/* Blank line. */
+		{
+			if (thisholepos < 0)
+				thisholepos = pos;
+			continue;
+		}
+
+		/* Strip off "s-" if we've seen a "sections" keyword */
+		if (gotsections && keyword[0] == 's' && keyword[1] == '-')
+			for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++)
+				;
+
+		/* If this is the start of a CD entry, see if it matches. */
+		if (! strcmp(keyword, "tracks"))
+		{
+			if (prefs == 2)
+				break;
+
+			/* Is this the end of a hole? */
+			if (holesize_wanted && (thisholepos >= 0))
+			{
+				/* Yep.  Is it better than the last one? */
+				if (pos - thisholepos < holesize && pos -
+						thisholepos >= holesize_wanted)
+				{
+					holepos = thisholepos;
+					holesize = pos - thisholepos;
+				}
+				thisholepos = -1;
+			}
+
+			/* Is it the start of the CD entries? */
+			if (firstpos == -1)
+				firstpos = pos;
+
+			/* Is this the end of the entry we really wanted? */
+			if (! searching)
+			{
+				rclen = pos - rcpos;
+				break;
+			}
+
+                        /* If we have a near match, indicate that we
+                            should stop reading tracks, etc now */
+                        if (searching == 2)
+                        {
+                            searching = 3;
+                            continue;
+                        }
+
+			fscanf(fp, "%d", &ntracks);
+
+			if (ntracks != cd->ntracks)
+			{
+chomp:
+				SWALLOW_LINE(fp);
+				continue;
+			}
+
+                        fudge = 0;
+                        maxfudge = (ntracks * fuzz_frames) >> 1;
+			track = 0;
+			for (i = 0; i < ntracks; i++)
+			{
+				fscanf(fp, "%d", &scratch);
+				if (scratch != cd->trk[track].start)
+                                {
+                                    sizediff = abs(scratch - cd->trk[track].start);
+                                    if (sizediff > fuzz_frames || 
+                                        (sizediff && scan))
+                                      break;
+                                    fudge += sizediff;
+                                }
+				while (cd->trk[++track].section > 1)
+					;
+			}
+			if (i != ntracks)
+				goto chomp;
+
+                        if (fudge > 0) /* best near match? */
+                        {
+                            if (fudge > maxfudge)
+                                goto chomp;
+                            if (bestfudge == 0 || fudge < bestfudge)
+                                bestfudge = fudge;
+                            else
+                                goto chomp;
+			    rcpos = pos;
+			    track = 0;
+                            searching = 2;
+                        }
+                        else /* probably exact match */
+                        {
+			    fscanf(fp, "%d", &scratch);
+
+			    if (scratch != -1 && scratch != cd->length)
+				    goto chomp;
+
+			    /* Found it! */
+			    rcpos = pos;
+			    track = 0;
+			    searching = 0;
+                        }
+
+			SWALLOW_LINE(fp);	/* Get rid of newline */
+		}
+
+		/* Global mode stuff goes here */
+		else if (! strcmp(keyword, "cddbprotocol"))
+		{
+			getc(fp);
+			i = getc(fp);	/* only first letter is used */
+		        cddb.protocol = i == 'c' ? 1 : 
+		                        i == 'h' ? 2 : 3 ;
+			do
+				i = getc(fp);
+			while (i != '\n' && i != EOF);
+		}
+
+		else if (! strcmp(keyword, "cddbserver"))
+		{
+			getc(fp);	/* lose the space */
+			if (cddb.cddb_server[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(cddb.cddb_server, 
+				      sizeof(cddb.cddb_server), fp);
+				if ((i = strlen(cddb.cddb_server)))
+					cddb.cddb_server[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "cddbmailadress"))
+		{
+			getc(fp);	/* lose the space */
+			if (cddb.mail_adress[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(cddb.mail_adress, 
+				      sizeof(cddb.mail_adress), fp);
+				if ((i = strlen(cddb.mail_adress)))
+					cddb.mail_adress[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "cddbpathtocgi"))
+		{
+			getc(fp);	/* lose the space */
+			if (cddb.path_to_cgi[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(cddb.path_to_cgi, 
+				      sizeof(cddb.path_to_cgi), fp);
+				if ((i = strlen(cddb.path_to_cgi)))
+					cddb.path_to_cgi[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "cddbproxy"))
+		{
+			getc(fp);	/* lose the space */
+			if (cddb.proxy_server[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(cddb.proxy_server, 
+				      sizeof(cddb.proxy_server), fp);
+				if ((i = strlen(cddb.proxy_server)))
+					cddb.proxy_server[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "whendone"))
+		{
+			getc(fp);
+			i = getc(fp);	/* only first letter is used */
+			if (cur_stopmode == -1)	/* user's setting preferred */
+				cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2;
+			do
+				i = getc(fp);
+			while (i != '\n' && i != EOF);
+		}
+
+		else if (! strcmp(keyword, "playnew"))
+		{
+			if (cur_playnew == -1)
+				cur_playnew = 1;
+			SWALLOW_LINE(fp);
+		}
+
+		/* If we're searching, skip to the next "tracks" line. */
+		else if (((searching & 1)|| scan) 
+                         && !(prefs && firstpos == -1))
+			SWALLOW_LINE(fp)
+
+		else if (! strcmp(keyword, "sections"))
+		{
+			gotsections = 1;
+			fscanf(fp, "%d", &ntracks);
+
+			free(trackmap);
+			trackmap = (int *) malloc(sizeof(int) *
+						(cur_ntracks + ntracks));
+			if (trackmap == NULL)
+			{
+				perror("section mapping");
+				exit(1);
+			}
+
+			/*
+			 * If sections are already defined, use these as a
+			 * reference, mapping this CD entry's section numbers
+			 * to the ones in core.
+			 *
+			 * Otherwise, split the CD up according to the sections
+			 * listed here.
+			 */
+			if (cur_nsections)
+			{
+				track = 0;
+				i = 0;
+				while (ntracks)
+				{
+					ntracks--;
+					fscanf(fp, "%d", &scratch);
+					while (scratch > cd->trk[track].start)
+					{
+						if (cd->trk[track].section < 2)
+							trackmap[i++] = track;
+						++track;
+
+						if (track == cur_ntracks)
+							break;
+					}
+
+					/* rc has later sections than db... */
+					if (track == cur_ntracks)
+						break;
+
+					/* Matches can be approximate */
+					if (scratch+75 > cd->trk[track].start &&
+					    scratch-75 < cd->trk[track].start)
+						trackmap[i++] = track++;
+					else
+						trackmap[i++] = -1;
+					
+					if (track == cur_ntracks)
+						break;
+				}
+
+				/* This only happens if track == cur_ntracks */
+				while (ntracks--)
+					trackmap[i++] = -1;
+
+				while (track < cur_ntracks)
+				{
+					if (cd->trk[track].section < 2)
+						trackmap[i++] = track;
+					track++;
+				}
+
+				track = 0;
+				SWALLOW_LINE(fp);
+			}
+			else
+			{
+				while (ntracks--)
+				{
+					fscanf(fp, "%d", &scratch);
+					split_trackinfo(scratch);
+				}
+
+				for (i = 0; i < cur_ntracks; i++)
+				{
+					trackmap[i] = i;
+					/* split_trackinfo() sets this */
+					cd->trk[i].contd = 0;
+				}
+
+				SWALLOW_LINE(fp);
+			}
+		}
+
+		else if (! strcmp(keyword, "track"))
+		{
+			char buf[502];
+
+			getc(fp);	/* lose the space */
+			/* don't overwrite existing track names. */
+			/* However, overwrite them if there was a "bad" fuzzy match before */
+			if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2))
+				SWALLOW_LINE(fp)
+			else if (cd->trk[trackmap[track]].songname &&
+					cd->trk[trackmap[track]].songname[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(buf, sizeof(buf), fp);
+				if( (i = strlen(buf)) )
+					buf[i - 1] = '\0';
+				wm_strmcpy(&cd->trk[trackmap[track]].songname,
+									buf);
+			}
+			track++;
+		}
+
+		else if (! strcmp(keyword, "playmode"))
+			fscanf(fp, "%d", &cd->playmode);
+
+		else if (! strcmp(keyword, "autoplay"))
+			cd->autoplay = 1;
+
+		else if (! strcmp(keyword, "cdname"))
+		{
+                        /* because of fuzzy matching that may change
+                           the disk contents, we reset everything when
+                           we find the name, in hopes that we will recover
+                           most, if not all, of the information from the
+                           file. */
+/*
+ * nasty bug was here. Was it? BUGBUGBUG
+ *
+ *			 wipe_cdinfo(); 
+ */			 trackmap = reset_tracks();
+
+			getc(fp);	/* lose the space */
+			/* don't overwrite existing cd name. */
+			if (cd->cdname[0] && (searching == 2))
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+                                if (searching > 1)
+                                {
+                                    strcpy(cd->cdname, "Probably://");
+				    fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp);
+                                } 
+                                else 
+                                {
+				    fgets(cd->cdname, sizeof(cd->cdname), fp);
+                                } 
+				if ( (i = strlen(cd->cdname)) )
+					cd->cdname[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "artist"))
+		{
+			getc(fp);	/* lose the space */
+			/* don't overwrite existing artist names. */
+			if (cd->artist[0])
+				do
+					i = getc(fp);
+				while (i != '\n' && i != EOF);
+			else
+			{
+				fgets(cd->artist, sizeof(cd->artist), fp);
+				if( (i = strlen(cd->artist)) )
+					cd->artist[i - 1] = '\0';
+			}
+		}
+
+		else if (! strcmp(keyword, "cdvolume"))
+		{
+			fscanf(fp, "%d", &cd->volume);
+			cd->volume = (cd->volume * 100) / 32;
+		}
+
+		else if (! strcmp(keyword, "dontplay"))
+		{
+			fscanf(fp, "%d", &i);
+			if (trackmap[i - 1] != -1)
+				cd->trk[trackmap[i - 1]].avoid = 1;
+		}
+
+		else if (! strcmp(keyword, "continue"))
+		{
+			if (trackmap[track - 1] != -1)
+				cd->trk[trackmap[track - 1]].contd = 1;
+		}
+
+		else if (! strcmp(keyword, "volume"))
+		{
+			fscanf(fp, "%d", &i);
+			if (trackmap[i - 1] == -1)
+				SWALLOW_LINE(fp)
+			else
+			{
+				i = trackmap[i - 1];
+				fscanf(fp, "%d", &cd->trk[i].volume);
+				cd->trk[i].volume = (cd->trk[i].volume*100)/32;
+				if (cd->trk[i].volume > 32)
+					cd->trk[i].volume = 0;
+			}
+		}
+
+		else if (! strcmp(keyword, "playlist"))
+		{
+			getc(fp);
+			fscanf(fp, "%s", listname);
+
+/* XXX take this out at some point */
+			if (! strcmp(listname, "Default"))
+				strcpy(listname, "List A");
+
+			for (i = 0; listname[i]; i++)
+				if (listname[i] == '_')
+					listname[i] = ' ';
+
+			l = new_list(cd, listname);
+			if (l == NULL)
+			{
+plnomem:
+				perror("playlist read");
+				exit(1);
+			}
+
+			fscanf(fp, "%d", &listsize);
+
+			l->list = malloc(sizeof(int) * (listsize + 1));
+			if (l->list == NULL)
+				goto plnomem;
+
+			/* Leave out tracks that weren't in .workmandb. */
+			j = 0;
+			for (i = 0; i < listsize; i++)
+			{
+				fscanf(fp, "%d", &scratch);
+				scratch = trackmap[scratch - 1];
+				if (scratch != -1)
+					l->list[j++] = scratch + 1;
+			}
+
+			l->list[j] = 0;
+		}
+
+		else if (! strcmp(keyword, "mark"))
+		{
+			int mark_val = -1, mark_namelen;
+			char mark_name[32];
+
+			fscanf(fp, "%d", &mark_val);
+			if (mark_val == -1)
+				goto chomp;
+
+			if (getc(fp) != ' ')
+				continue;
+
+			fgets(mark_name, sizeof(mark_name), fp);
+			if( ( mark_namelen = strlen(mark_name)) )
+				mark_name[mark_namelen - 1] = '\0';
+
+			if (! strcmp(mark_name, "START"))
+				set_abtimer(0, mark_val);
+			else if (! strcmp(mark_name, "END"))
+				set_abtimer(1, mark_val);
+		}
+
+		/* Unrecognized keyword.  Put it in the right place. */
+		else
+		{
+			char	**buf, input[BUFSIZ];
+
+			if (track && trackmap[track - 1] == -1)
+			{
+				SWALLOW_LINE(fp);
+				continue;
+			}
+
+			i = track ? trackmap[track - 1] : 0;
+			buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc :
+				i ? &cd->trk[i].otherdb : &cd->otherdb;
+			if (firstpos == -1) {
+				if (prefs) {
+					buf = &otherrc;
+				} else {
+					goto chomp;
+				} /* if() else */
+                        } /* if() */
+			wm_strmcat(buf, keyword);
+			do {
+				input[sizeof(input) - 1] = 'x';
+				fgets(input, sizeof(input), fp);
+				wm_strmcat(buf, input);
+			} while (input[sizeof(input) - 1] != 'x');
+		}
+	}
+
+	if (rclen == 0 && !searching)
+		rclen = pos - rcpos;
+
+        if (searching > 1) /* A near match has been found. Good enough. */
+            searching = 0;
+
+        cddb_struct2cur();
+	return (! searching);
+
+} /* search_db() */
+
+/*
+ * Delay some amount of time without using interval timers.
+ */
+void
+spinwheels(int secs) {
+	struct timeval	tv;
+
+	tv.tv_usec = 0;
+	tv.tv_sec = secs;
+	select(0, NULL, NULL, NULL, &tv);
+} /* spinwheels() */
+
+/*
+ * lockit(fd, type)
+ *
+ * fd    file descriptor
+ * type  lock type
+ *
+ * Lock a file.  Time out after a little while if we can't get a lock;
+ * this usually means the locking system is broken.
+ *
+ * Unfortunately, if there are lots of people contending for a lock,
+ * this can result in the file not getting locked when it probably should.
+ */
+int
+lockit(int fd, int type)
+{
+	struct flock	fl;
+	int		result, timer = 0;
+
+	if (suppress_locking)
+		return (0);
+
+	fl.l_type = type;
+	fl.l_whence = 0;
+	fl.l_start = 0;
+	fl.l_len = 0;
+
+	while ((result = fcntl(fd, F_SETLK, &fl)) < 0)
+	{
+		if (errno != EACCES || errno != EAGAIN)
+			break;
+		if (timer++ == 30)
+		{
+			errno = ETIMEDOUT;
+			break;
+		}
+
+		spinwheels(1);
+	}
+
+	return (result);
+} /* lockit */
+
+/*
+ * Search all the database files and our personal preference file for
+ * more information about the current CD.
+ */
+void
+load( void )
+{
+	FILE		*fp;
+	char		**dbfile;
+	int		locked = 0;
+	int		dbfound = 0, *trklist, i;
+	unsigned long	dbpos;
+
+/* This is some kind of profiling code. I don't change it
+   to wm_lib_message() for now... */
+#ifdef DEBUG
+	long		t1, t2;
+	if( getenv( "WORKMAN_DEBUG" ) != NULL )
+	  {
+		time(&t1);
+		printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1);
+		fflush(stdout);
+	  }	
+#endif
+
+	dbfile = databases;
+
+	found_in_db = 0;
+
+	/* Turn the cd->trk array into a simple array of ints. */
+	trklist = (int *)malloc(sizeof(int) * cd->ntracks);
+	for (i = 0; i < cd->ntracks; i++)
+		trklist[i] = cd->trk[i].start;
+
+	do {
+		if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist,
+				cd->length * 75, 0, &dbpos) == 0)
+			dbfound = 1;
+
+		fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL;
+		if (fp != NULL)
+		{
+			if (lockit(fileno(fp), F_RDLCK))
+				perror("Couldn't get read (db) lock");
+			else
+				locked = 1;
+
+			if (dbfound)
+				fseek(fp, dbpos, 0);
+
+			if (search_db(fp, 0, 0, 0))
+			{
+				found_in_db = 1;
+				cd->whichdb = *dbfile;
+			}
+
+			if (locked && lockit(fileno(fp), F_UNLCK))
+				perror("Couldn't relinquish (db) lock");
+
+			fclose(fp);
+		}
+	} while (*++dbfile != NULL && cd->whichdb == NULL);
+
+#ifdef DEBUG
+	if( getenv( "WORKMAN_DEBUG" ) != NULL )
+	  {
+		time(&t2);
+		printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+		fflush(stdout);
+	  }	
+#endif
+
+	fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+	if (fp != NULL)
+	{
+		locked = 0;
+		if (lockit(fileno(fp), F_RDLCK))
+			perror("Couldn't get read (rc) lock");
+		else
+			locked = 1;
+
+		rcpos = 0;
+		found_in_rc = search_db(fp, 1, 0, 0);
+		if (! found_in_rc)
+			cd->autoplay = wm_db_get_playnew();
+
+		if (locked && lockit(fileno(fp), F_UNLCK))
+			perror("Couldn't relinquish (rc) lock");
+
+		fclose(fp);
+	}
+
+	free(trklist);
+
+	if (cur_playnew == -1)
+		cur_playnew = 0;
+
+#ifdef DEBUG
+	if( getenv( "WORKMAN_DEBUG" ) != NULL )
+	  {
+		time(&t2);
+		printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1);
+		fflush(stdout);
+	  }	
+#endif
+} /* load() */
+
+/*
+ * Load program settings from the rcfile.
+ */
+void
+load_settings( void )
+{
+	FILE    *fp;
+	int     locked;
+
+	fp = rcfile ? open_rcfile(rcfile, "r") : NULL;
+	if (fp != NULL)
+	{
+		locked = 0;
+		if (lockit(fileno(fp), F_RDLCK))
+			perror("Couldn't get read (rc) lock");
+		else
+			locked = 1;
+
+		rcpos = 0;
+		found_in_rc = search_db(fp, 2, 0, 0);
+		if (! found_in_rc)
+			cd->autoplay = wm_db_get_playnew();
+
+		if (locked && lockit(fileno(fp), F_UNLCK))
+			perror("Couldn't relinquish (rc) lock");
+
+		fclose(fp);
+	}
+} /* load_settings() */
+
+/*
+ * save_globals()
+ *
+ * Save the global preferences, scooting CD entries to the end if needed.
+ * The assumption here is that the rcfile is locked, and that firstpos has
+ * been set by a previous scan.
+ */
+void
+save_globals(FILE *fp)
+{
+	char	*globes = NULL, *cdentry = NULL, temp[100];
+	long	curpos;
+	int	globesize, hit_cdent = 0, c = 0;
+
+	if (otherrc)
+		wm_strmcpy(&globes, otherrc);
+
+	if (cddb.protocol)
+	{
+		sprintf(temp, "cddbprotocol ");
+		switch(cddb.protocol)
+		{
+		 case 1: /* cddbp */
+		    sprintf(temp + strlen(temp), "cddbp\n");
+		    break;
+		 case 2: /* http */
+		    sprintf(temp + strlen(temp), "http\n");
+		    break;
+		 case 3: /* proxy */
+		    sprintf(temp + strlen(temp), "proxy\n");
+		    break;
+		 default:
+		    break;
+		}
+		wm_strmcat(&globes, temp);
+	    
+		if(cddb.mail_adress[0])
+	 	{
+			sprintf(temp,"cddbmailadress %s\n",
+				cddb.mail_adress);
+			wm_strmcat(&globes, temp);
+		}
+
+		if(cddb.cddb_server[0])
+	 	{
+			sprintf(temp,"cddbserver %s\n",
+				cddb.cddb_server);
+			wm_strmcat(&globes, temp);
+		}
+
+		if(cddb.path_to_cgi[0])
+	 	{
+			sprintf(temp,"cddbpathtocgi %s\n",
+				cddb.mail_adress);
+			wm_strmcat(&globes, temp);
+		}
+
+		if(cddb.proxy_server[0])
+	 	{
+			sprintf(temp,"cddbproxy %s\n",
+				cddb.mail_adress);
+			wm_strmcat(&globes, temp);
+		}
+	}
+
+	if (cur_stopmode == 1 || cur_stopmode == 2)
+	{
+		sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" :
+			"eject");
+		wm_strmcat(&globes, temp);
+	}
+
+	if (cur_playnew == 1)
+		wm_strmcat(&globes, "playnew\n");
+
+	curpos = firstpos;
+	if (curpos < 0)
+		curpos = 0;
+
+	fseek(fp, curpos, SEEK_SET);
+
+	if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0))
+	{
+		while (1)
+		{
+			temp[sizeof(temp)-1] = 'x';
+
+			if (fgets(temp, sizeof(temp), fp) == NULL)
+			{
+				fseek(fp, 0, SEEK_SET);
+				if (globes != NULL)
+				{
+					fwrite(globes, globesize, 1, fp);
+					free(globes);
+				}
+				if (cdentry != NULL)
+				{
+					fwrite(cdentry, strlen(cdentry), 1, fp);
+					free(cdentry);
+				}
+				return;
+			}
+
+			if (! strncmp(temp, "tracks ", 7))
+			{
+				hit_cdent = 1;
+				if (curpos >= globesize)
+					break;
+			}
+
+			if (! hit_cdent)
+			{
+				curpos += strlen(temp);
+				if (temp[sizeof(temp)-1] == '\0')
+					while ((c = getc(fp)) != '\n' &&
+								c != EOF)
+						curpos++;
+				if (c == '\n')
+					curpos++;
+
+				continue;
+			}
+
+			wm_strmcat(&cdentry, temp);
+			curpos += strlen(temp);
+			while (temp[sizeof(temp)-1] == '\0')
+			{
+				temp[sizeof(temp)-1] = 'x';
+				if (fgets(temp, sizeof(temp), fp) == NULL)
+					break;
+				wm_strmcat(&cdentry, temp);
+				curpos += strlen(temp);
+			}
+		} 
+
+		if (cdentry != NULL)
+		{
+			fseek(fp, 0, SEEK_END);
+			fwrite(cdentry, strlen(cdentry), 1, fp);
+			free(cdentry);
+		}
+	}
+
+	if (globes != NULL)
+	{
+		fseek(fp, 0, SEEK_SET);
+		fwrite(globes, globesize, 1, fp);
+		free(globes);
+	}
+
+	while (globesize++ < curpos)
+		putc('\n', fp);
+} /* save_globals() */
+
+/*
+ * save_entry()
+ *
+ * Save the CD information to one database.
+ *
+ *	filename	Database to save to.
+ *	pref		0 for hard data, 1 for preferences.
+ *
+ * If an entry for this CD exists already, overwrite it with the new entry
+ * if the new entry is the same size or smaller, or with newlines if the new
+ * entry is larger (in which case the new entry is appended to the file.)
+ *
+ * Also, if the preference information is being updated, save it to the
+ * file while we've got it locked.  Scoot stuff from the beginning of
+ * the file to the end as needed to facilitate this.
+ *
+ * XXX Preference-saving should probably be done elsewhere, like in an
+ * Apply button on the Goodies popup, and in any case needs to go to a
+ * different file (.Xdefaults?)
+ *
+ * Returns 0 on success.
+ */
+int
+save_entry(char *filename, int pref)
+{
+	FILE		*fp;
+	char		*buf;
+	int		len, i, locked = 0;
+
+
+	if( filename == NULL )
+		return (-1);
+
+	fp = open_rcfile(filename, "r+");
+	if (fp == NULL)
+	{
+		if (errno == ENOENT)	/* doesn't exist already */
+			fp = open_rcfile(filename, "w");
+		if (fp == NULL)
+			return (-1);
+	}
+
+	if (lockit(fileno(fp), F_WRLCK))
+		perror("Warning: Couldn't get write lock");
+	else
+		locked = 1;
+
+	buf = print_cdinfo(cd, pref);
+	len = strlen(buf);	/* doesn't return if there's an error */
+
+	rcpos = -1;
+	search_db(fp, pref, 1, len);
+	if (rcpos != -1)		/* XXX */
+	{
+		/*
+		 * Jump to the entry's position in the database file, if
+		 * it was found.
+		 */
+		fseek(fp, rcpos, SEEK_SET);
+
+		if (rclen >= len && holepos == -1)
+		{
+			/*
+			 * If the new entry will fit in the space occupied by
+			 * the old one, overwrite the old one and make a hole
+			 * of the appropriate size at its end.
+			 *
+			 * No need to update the index file in this case, as
+			 * the entry's position hasn't changed.
+			 */
+			fputs(buf, fp);
+			for (i = len; i < rclen; i++)
+				fputc('\n', fp);
+		}
+		else
+		{
+			/*
+			 * Overwrite the old entry with a hole and delete
+			 * its pointer in the index file.
+			 */
+			for (i = 0; i < rclen; i++)
+				fputc('\n', fp);
+			idx_delete_entry(filename, cd->trk[cd->ntracks-1].start,
+					0, rcpos);
+
+			rcpos = -1;
+		}
+	}
+
+	/*
+	 * Old entry wasn't found, or its new version wouldn't fit where
+	 * the old one was.
+	 */
+	if (rcpos == -1)
+	{
+		/*
+		 * Write the new entry in a hole, if there is one,
+		 * or at the end of the file.
+		 */
+		if (holepos >= 0)
+		{
+			fseek(fp, holepos, SEEK_SET);
+			if (holepos < firstpos)
+				firstpos = holepos;
+		}
+		else
+		{
+			fseek(fp, 0, SEEK_END);
+			holepos = ftell(fp);
+		}
+		fputs(buf, fp);
+
+		/*
+		 * Write a new index entry for this CD.
+		 */
+		idx_write_entry(filename, cd->trk[cd->ntracks - 1].start,
+			holepos);
+	}
+
+	if (pref)
+		save_globals(fp);
+
+	fflush(fp);
+
+	if (locked && lockit(fileno(fp), F_UNLCK))
+		perror("Warning: Couldn't relinquish write lock");
+
+	fclose(fp);
+
+	return (0);
+} /* save_entry() */
+
+/*
+ * save()
+ *
+ * Save CD information to the appropriate datafile (the first file in the
+ * list, unless the entry came from another database file) and to the
+ * personal prefs file.
+ */
+int
+save( void )
+{
+
+	if( wm_db_save_disabled == FALSE )
+	{
+		if (save_entry(rcfile, 1))
+			return (0);
+
+		if (cd->whichdb == NULL || access(cd->whichdb, W_OK))
+			cd->whichdb = databases[0];
+
+		if (save_entry(cd->whichdb, 0))
+			return (0);
+
+		return( WM_DB_SAVE_ERROR );
+	} else {
+		return( WM_DB_SAVE_DISABLED );
+	}
+} /* save() */
diff --git a/ascd/libworkman/drv_sony.c b/ascd/libworkman/drv_sony.c
new file mode 100644
index 0000000..338ecc5
--- /dev/null
+++ b/ascd/libworkman/drv_sony.c
@@ -0,0 +1,173 @@
+/*
+ * $Id: drv_sony.c,v 1.3 1999/02/14 09:50:42 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Vendor-specific drive control routines for Sony CDU-8012 series.
+ */
+
+static char drv_sony_id[] = "$Id: drv_sony.c,v 1.3 1999/02/14 09:50:42 dirk Exp $";
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define PAGE_AUDIO		0x0e
+
+/* local prototypes */
+static int	sony_init( struct wm_drive *d );
+static int	sony_set_volume(struct wm_drive *d, int left, int right );
+static int	sony_get_volume( struct wm_drive *d, int *left, int *right );
+
+
+extern int	min_volume, max_volume;
+
+struct wm_drive sony_proto = {
+	-1,			/* fd */
+	"Sony",			/* vendor */
+	"CDU-8012",		/* model */
+	"",			/* revision */
+	NULL,			/* aux */
+	NULL,			/* daux */
+
+	sony_init,		/* functions... */
+	gen_get_trackcount,
+	gen_get_cdlen,
+	gen_get_trackinfo,
+	gen_get_drive_status,
+	sony_get_volume,
+	sony_set_volume,
+	gen_pause,
+	gen_resume,
+	gen_stop,
+	gen_play,
+	gen_eject,
+	gen_closetray
+};
+
+/*
+ * Initialize the driver.
+ */
+static int sony_init( struct wm_drive *d ) {
+
+/* Sun use Sony drives as well */
+#if defined(SYSV) && defined(CODEC)
+        codec_init();
+#endif
+	min_volume = 128;
+	max_volume = 255;
+	return( 0 );
+} /* sony_init() */
+
+/*
+ * On the Sony CDU-8012 drive, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom.  To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting.  Additionally, only the top half
+ * of the volume scale is valid; the bottom half is all silent.  The actual
+ * formula looks like
+ *
+ *     max^2 - (max - vol)^2   max
+ * v = --------------------- + ---
+ *            max * 2           2
+ *
+ * Where "max" is the maximum value of the volume scale, usually 100.
+ */
+static int
+scale_volume(vol, max)
+	int	vol, max;
+{
+	vol = (max*max - (max - vol) * (max - vol)) / max;
+	return ((vol + max) / 2);
+}
+
+/*
+ * Given a value between min_volume and max_volume, return the standard-scale
+ * volume value needed to achieve that hardware value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+	int	cd_vol, max;
+{
+	int	vol = 0, top = max, bot = 0, scaled = 0;
+
+	cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol <= scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	/* Might have looked down too far for repeated scaled values */
+	if (cd_vol < scaled)
+		vol++;
+
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*
+ * Get the volume.  Sun's CD-ROM driver doesn't support this operation, even
+ * though their drive does.  Dumb.
+ */
+static int
+sony_get_volume( struct wm_drive *d, int *left, int *right )
+{
+	unsigned char	mode[16];
+
+	/* Get the current audio parameters first. */
+	if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+		return (-1);
+
+	*left = unscale_volume(mode[9], 100);
+	*right = unscale_volume(mode[11], 100);
+
+	return (0);
+}
+
+/*
+ * Set the volume using the wacky scale outlined above.  The Sony drive
+ * responds to the standard set-volume command.
+ */
+static int
+sony_set_volume(struct wm_drive *d, int left, int right )
+{
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+	return (gen_set_volume(d, left, right));
+}
diff --git a/ascd/libworkman/drv_toshiba.c b/ascd/libworkman/drv_toshiba.c
new file mode 100644
index 0000000..3412182
--- /dev/null
+++ b/ascd/libworkman/drv_toshiba.c
@@ -0,0 +1,154 @@
+/*
+ * $Id: drv_toshiba.c,v 1.3 1999/02/14 09:50:42 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Vendor-specific drive control routines for Toshiba XM-3401 series.
+ */
+
+static char drv_toshiba_id[] = "$Id: drv_toshiba.c,v 1.3 1999/02/14 09:50:42 dirk Exp $";
+
+#include <stdio.h>
+#include <errno.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+
+#define	SCMD_TOSH_EJECT		0xc4
+
+/* local prototypes */
+static int	tosh_init( struct wm_drive *d );
+static int	tosh_eject( struct wm_drive *d );
+static int	tosh_set_volume( struct wm_drive *d, int left, int right );
+static int	tosh_get_volume( struct wm_drive *d, int *left, int *right );
+
+struct wm_drive toshiba_proto = {
+	-1,			/* fd */
+	"Toshiba",		/* vendor */
+	"",			/* model */
+	"",			/* revision */
+	NULL,			/* aux */
+	NULL,			/* daux */
+
+	tosh_init,		/* functions... */
+	gen_get_trackcount,
+	gen_get_cdlen,
+	gen_get_trackinfo,
+	gen_get_drive_status,
+	tosh_get_volume,
+	tosh_set_volume,
+	gen_pause,
+	gen_resume,
+	gen_stop,
+	gen_play,
+	tosh_eject,
+	gen_closetray
+};
+
+/*
+ * Initialize the driver.
+ */
+static int
+tosh_init( struct wm_drive *d )
+{
+	extern int	min_volume;
+
+/* Sun use Toshiba drives as well */
+#if defined(SYSV) && defined(CODEC)
+        codec_init();
+#endif
+	min_volume = 0;
+	return ( 0 );
+}
+
+/*
+ * Send the Toshiba code to eject the CD.
+ */
+static int
+tosh_eject( struct wm_drive *d )
+{
+	return (sendscsi(d, NULL, 0, 0, SCMD_TOSH_EJECT, 1, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Set the volume.  The low end of the scale is more sensitive than the high
+ * end, so make up for that by transforming the volume parameters to a square
+ * curve.
+ */
+static int
+tosh_set_volume( struct wm_drive *d, int left, int right )
+{
+	left = (left * left * left) / 10000;
+	right = (right * right * right) / 10000;
+	return (gen_set_volume(d, left, right));
+}
+
+/*
+ * Undo the transformation above using a binary search (so no floating-point
+ * math is required.)
+ */
+static int
+unscale_volume(cd_vol, max)
+	int	cd_vol, max;
+{
+	int	vol = 0, top = max, bot = 0, scaled = 0;
+
+	/*cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;*/
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = (vol * vol) / max;
+		if (cd_vol <= scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	/* Might have looked down too far for repeated scaled values */
+	if (cd_vol < scaled)
+		vol++;
+
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*
+ * Get the volume.
+ */
+static int
+tosh_get_volume( struct wm_drive *d, int *left, int *right )
+{
+	int		status;
+
+	status = gen_get_volume(d, left, right);
+	if (status < 0)
+		return (status);
+	*left = unscale_volume(*left, 100);
+	*right = unscale_volume(*right, 100);
+
+	return (0);
+}
diff --git a/ascd/libworkman/include/wm_cdda.h b/ascd/libworkman/include/wm_cdda.h
new file mode 100644
index 0000000..831b19e
--- /dev/null
+++ b/ascd/libworkman/include/wm_cdda.h
@@ -0,0 +1,117 @@
+#ifndef WM_CDDA_H
+#define WM_CDDA_H
+/*
+ * $Id: wm_cdda.h,v 1.2 1999/02/14 09:50:46 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * Information about a particular block of CDDA data.
+ */
+struct cdda_block {
+	unsigned char	status;		/* see below */
+	unsigned char	track;
+	unsigned char	index;
+	unsigned char	minute;
+	unsigned char	second;
+	unsigned char	frame;
+
+	/* Average volume levels, for level meters */
+	unsigned char	lev_chan0;
+	unsigned char	lev_chan1;
+
+	/* Current volume setting (0-255) */
+	unsigned char	volume;
+
+	/* Current balance setting (0-255, 128 = balanced) */
+	unsigned char	balance;
+};
+
+/*
+ * cdda_block status codes.
+ */
+#define WMCDDA_ERROR	0	/* Couldn't read CDDA from disc */
+#define WMCDDA_OK	1	/* Read this block successfully (raw data) */
+#define WMCDDA_PLAYED	2	/* Just played the block in question */
+#define WMCDDA_STOPPED	3	/* Block data invalid; we've just stopped */
+#define WMCDDA_ACK	4	/* Acknowledgement of command from parent */
+#define WMCDDA_DONE	5	/* Chunk of data is done playing */
+#define WMCDDA_EJECTED	6	/* Disc ejected or offline */
+
+/*
+ * Enable or disable CDDA building depending on platform capabilities, and
+ * determine endianness based on architecture.  (Gross!)
+ *
+ * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be
+ * renamed. At least Linux does have bytesex.h and endian.h for easy
+ * byte-order examination.
+ */
+
+#ifdef sun
+# ifdef SYSV
+#  include <sys/types.h>
+#  include <sys/cdio.h>
+#  ifndef CDROMCDDA
+#   undef BUILD_CDDA
+#  endif
+#  ifdef i386
+#   define WM_LITTLE_ENDIAN 1
+#   define WM_BIG_ENDIAN 0
+#  else
+#   define WM_BIG_ENDIAN 1
+#   define WM_LITTLE_ENDIAN 0
+#  endif
+# else
+#  undef BUILD_CDDA
+# endif
+#endif
+
+/* Linux only allows definition of endianness, because there's no
+ * standard interface for CDROM CDDA functions that aren't available
+ * if there is no support.
+ */
+#ifdef linux
+# include <bytesex.h>
+# include <endian.h>
+/*
+ * XXX could this be a problem? The results are only 0 and 1 because
+ * of the ! operator. How about other linux compilers than gcc ?
+ */
+# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN)
+# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN)
+#endif
+
+/*
+ * The following code shouldn't take effect now. 
+ * In 1998, the WorkMan platforms don't support __PDP_ENDIAN
+ * architectures.
+ *
+ */
+
+#if !WM_LITTLE_ENDIAN
+#  if !WM_BIG_ENDIAN
+#    error yet unsupported architecture
+	foo bar this is to stop the compiler. 
+#  endif
+#endif
+#endif /* WM_CDDA_H */
diff --git a/ascd/libworkman/include/wm_cddb.h b/ascd/libworkman/include/wm_cddb.h
new file mode 100644
index 0000000..bd5b4f9
--- /dev/null
+++ b/ascd/libworkman/include/wm_cddb.h
@@ -0,0 +1,42 @@
+#ifndef WM_CDDB_H
+#define WM_CDDB_H
+/*
+ * $Id: wm_cddb.h,v 1.2 1999/02/14 09:50:46 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+extern struct wm_cddb cddb;
+extern int cur_cddb_protocol;
+extern char *cur_cddb_server, *cur_cddb_mail_adress,
+	*cur_cddb_path_to_cgi, *cur_cddb_proxy_server;
+
+unsigned int cddb_discid();
+void cddb_struct2cur();
+void cddb_cur2struct();
+void cddb_select();
+void connect_cddb();
+void update_cddbserver();
+void cddb_request();
+
+#endif /* WM_CDDB_H */
diff --git a/ascd/libworkman/include/wm_cdinfo.h b/ascd/libworkman/include/wm_cdinfo.h
new file mode 100644
index 0000000..f783465
--- /dev/null
+++ b/ascd/libworkman/include/wm_cdinfo.h
@@ -0,0 +1,78 @@
+#ifndef WM_CDINFO_H
+#define WM_CDINFO_H
+/*
+ * $Id: wm_cdinfo.h,v 1.5 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes from cdinfo.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+extern char *cur_trackname;		/* Take a guess */
+extern int cur_index;			/* Current index mark */
+extern int cur_frame;			/* Current frame number */
+extern struct wm_play *playlist;		/* = NULL */
+extern struct wm_cdinfo thiscd;
+extern struct wm_cdinfo *cd;
+extern int cur_track;			/* Current track number, starting at 1 */
+extern char *cur_artist;		/* Name of current CD's artist */
+extern char cur_avoid;			/* Avoid flag */
+extern char cur_contd;			/* Continued flag */
+extern char *cur_cdname;		/* Album name */
+extern int cur_nsections;		/* Number of sections currently defined */
+extern int exit_on_eject;
+extern int cur_track;
+extern int cur_pos_abs;
+extern int cur_pos_rel;
+extern int cur_tracklen;
+extern int cur_cdlen;
+extern enum wm_cd_modes cur_cdmode;
+extern int cur_ntracks;
+extern int cur_lasttrack;
+extern int cur_firsttrack;
+extern int cur_listno;
+extern int cur_stopmode;
+
+void	wipe_cdinfo( void );
+void	play_next_entry( int forward );
+void	make_playlist( int playmode, int starttrack );
+int	get_autoplay( void );
+int	get_playmode( void );
+void	pl_find_track( int track );
+void	play_prev_track( int forward );
+void	play_next_track( int forward );
+int	tracklen( int num );
+int	get_default_volume( int track );
+int	split_trackinfo( int pos );
+int	remove_trackinfo( int num );
+void	freeup( char **x );
+int	get_runtime( void );
+char   *trackname( int num );
+void	stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode );
+void	stash_trkinfo( int track, char *songname, int contd, int avoid );
+int	get_avoid( int num );
+int	get_contd( int num );
+void	default_volume( int track, int vol );
+char   *listentry( int num );
+
+#endif /* WM_CDINFO_H */
diff --git a/ascd/libworkman/include/wm_cdrom.h b/ascd/libworkman/include/wm_cdrom.h
new file mode 100644
index 0000000..7d0dca4
--- /dev/null
+++ b/ascd/libworkman/include/wm_cdrom.h
@@ -0,0 +1,64 @@
+#ifndef WM_CDROM_H
+#define WM_CDROM_H
+/*
+ * $Id: wm_cdrom.h,v 1.6 1999/05/05 16:09:55 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes from cdrom.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#define WM_CDS_NO_DISC		0
+#define WM_CDS_DISC_READY	1
+#define WM_CDS_JUST_INSERTED	2
+
+#define WM_STR_GENVENDOR	"Generic"
+#define WM_STR_GENMODEL		"drive"
+#define WM_STR_GENREV		"type"
+
+extern int wm_cd_cur_balance;
+
+
+char *	wm_drive_vendor( void );
+char *	wm_drive_model( void );
+char *	wm_drive_revision( void );
+void 	wm_drive_settype( char *vendor, char *model, char *revision );
+
+int	wm_cd_status( void );
+
+void	wm_cd_play( int start, int pos, int end );
+void	wm_cd_play_chunk( int start, int end, int realstart );
+void	wm_cd_play_from_pos( int pos );
+void	wm_cd_pause( void );
+void	wm_cd_stop( void );
+int	wm_cd_eject( void );
+int     wm_cd_closetray( void );
+int	wm_cd_read_initial_volume( int max );
+
+/*
+ * Following are the missing to rename.
+ */
+int	find_trkind( int track, int index, int start );
+void	cd_volume( int vol, int bal, int max );
+
+#endif /* WM_CDROM_H */
diff --git a/ascd/libworkman/include/wm_config.h b/ascd/libworkman/include/wm_config.h
new file mode 100644
index 0000000..c04a0e5
--- /dev/null
+++ b/ascd/libworkman/include/wm_config.h
@@ -0,0 +1,357 @@
+#ifndef WM_CONFIG_H
+#define WM_CONFIG_H
+/*
+ * $Id: wm_config.h,v 1.5 1999/05/05 16:34:22 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ **********************************************************************
+ *
+ * This file consists of several parts. First, there's a generic,
+ * platform independent part. Set needed options there.
+ * The following parts are platform dependent. You may search for the
+ * names listed below and then set your OS specific options there.
+ * Don't be surprised, if there are no options for your OS. They aren't
+ * needed in any case.
+ * 
+ * The default values should produce a functional WorkMan on every
+ * platform.
+ *
+ *********************
+ * Current platforms:
+ *********************
+ * BSD386
+ * FreeBSD
+ * HP-UX
+ * Irix (SGI)
+ * Linux
+ * News  (Sony NewsOS)
+ * OpenBSD
+ * OSF1
+ * Sun (SunOS/Solaris, Sparc or x86)
+ * SVR4
+ * Ultrix
+ * AIX
+ *
+ * The above order corresponds with the order of the platform specific
+ * options below.
+ */
+
+/******************************************************************
+ * generic options
+ ******************************************************************
+ **   **  ***  ****    **  ****   **    *    **   **    **   *   **
+ *  *  *  ***  ****  *  *  ***  *  **  **  ***  *  *  *  *  * *  **
+ *     *  ***  ****    **  ***     **  **   **  *  *    **  * *  **
+ *  *  *  ***  ****  ****  ***  *  **  **  ***  *  *  *  *  ***  **
+ *  *  *    *    **  ****    *  *  **  **  ****   **  *  *  ***  **
+ ******************************************************************/
+
+/*
+ * This option is obvious. But please do not forget the original
+ * WorkMan version string if you need support.
+ */
+
+#define WORKMAN_NAME "LibWorkMan"
+#define WORKMAN_VERSION "1.4.0"
+ 
+/*
+ * If your CD-ROM drive closes its tray if the device is opened, then
+ * the next define can make WorkMans "Eject" button an "open/close"
+ * button. If it disturbs you, just comment it out.
+ *
+ * ### this is preliminary. It may have no effect for you ###
+ */
+#define CAN_CLOSE 1
+
+/*
+ * Define the following if you want the balance slider to
+ * decrease one channel's volume while increasing the other's
+ */
+/* #define SYMETRIC_BALANCE 1 */
+
+
+/*
+ * Define this if you want CDDA support. Supported systems are:
+ *
+ * - Solaris (2.4 or newer)
+ * --> Linux is on the way. Don't define it now. It would lead to errors only.
+ */
+/*#define BUILD_CDDA 1*/
+
+/*
+ *
+ * This is for experimental cddb support.
+ * This activates the UI component.
+ */
+
+#define CDDB_IN_UI 1
+
+
+/******************************************************************
+ * BSD386
+ ******************************************************************
+ ***     ****    ***    *******  **    ****    ****    ************
+ ***  **  **  ******  *  *****  ******  **  **  **  ***************
+ ***    ******  ****  **  ***  *****  *****    ***     ************
+ ***  **  ******  **  *  ***  ********  **  **  **  **  ***********
+ ***     ****    ***    ***  ******    ****    ****    ************
+ ******************************************************************/
+#ifdef __bsdi__
+
+/*
+ * This lets you use the SoundBlaster Mixer on BSD/386
+ */
+#define SOUNDBLASTER 1
+
+#endif /* __bsdi__ (BSD/386) */
+
+/******************************************************************
+ * FreeBSD
+ ******************************************************************
+ ***      **     ***      **      **     ****    ***    ***********
+ ***  ******  **  **  ******  ******  **  **  ******  *  **********
+ ***    ****     ***    ****    ****    ******  ****  **  *********
+ ***  ******  **  **  ******  ******  **  ******  **  *  **********
+ ***  ******  **  **      **      **     ****    ***    ***********
+ ******************************************************************/
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+
+#define DEFAULT_CD_DEVICE	"/dev/rmatcd0c"
+
+#endif /* freebsd */
+
+/******************************************************************
+ * HP-UX
+ ******************************************************************
+ ***  **  **     *********  **  **  **  ***************************
+ ***  **  **  **  ********  **  ***    ****************************
+ ***      **     ***    **  **  ****  *****************************
+ ***  **  **  ************  **  ***    ****************************
+ ***  **  **  *************    ***  **  ***************************
+ ******************************************************************/
+#if defined(hpux) || defined (__hpux)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rscsi"
+
+#endif /* hpux */
+
+/******************************************************************
+ * Irix
+ ******************************************************************
+ ***      **     ***      **  **  *********************************
+ *****  ****  **  ****  *****    **********************************
+ *****  ****     *****  ******  ***********************************
+ *****  ****  **  ****  *****    **********************************
+ ***      **  **  **      **  **  *********************************
+ ******************************************************************/
+#if defined(sgi)
+
+#define DEFAULT_CD_DEVICE	"/dev/scsi/sc0d6l0"
+
+#endif /* sgi IRIX */
+
+/******************************************************************
+ * Linux
+ ******************************************************************
+ ***  ******      **   ***  **  **  **  **  ***********************
+ ***  ********  ****    **  **  **  ***    ************************
+ ***  ********  ****  *  *  **  **  ****  *************************
+ ***  ********  ****  **    **  **  ***    ************************
+ ***      **      **  ***   ***    ***  **  ***********************
+ ******************************************************************/
+#ifdef linux
+
+/*
+ * Uncomment the following line to have WorkMan send SCSI commands
+ *  directly to the CD-ROM drive.  If you have a SCSI drive you 
+ * probably want this, but it will cause WorkMan to not work on IDE
+ * drives.
+ */
+#define LINUX_SCSI_PASSTHROUGH 1
+
+/*
+ * Which device should be opened by WorkMan at default?
+ */
+#define DEFAULT_CD_DEVICE	"/dev/sbpcd"
+
+/*
+ * Uncomment the following if you use the sbpcd or mcdx device driver.
+ * It shouldn't hurt if you use it on other devices. It'll be nice to
+ * hear from non-sbpcd (or mcdx) users if this is right.
+ */
+/*#define SBPCD_HACK 1*/
+
+/*
+ * Linux Soundcard support
+ */
+#define OSS_SUPPORT 1
+
+/*
+ * This has nothing to do with the above.
+ */
+
+/* #define CURVED_VOLUME */
+ 
+/*
+ * Uncomment the following if you want to try out a better responding
+ * WorkMan, especially with IDE drives. This may work with non-IDE
+ * drives as well. But it may be possible, that it doesn't work at all.
+ * If your drive/driver combination cannot handle the faster access,
+ * the driver will usually hang and you have to reboot your machine.
+ */
+/* #define FAST_IDE 1 */
+
+/*
+ * There are two alternative ways of checking a device containing a
+ * mounted filesystem. Define BSD_MOUNTTEST for the test using
+ * getmntent(). Undefine it for using the SVR4 ustat().
+ * I built in the choice, because it's not clear which method should
+ * be used in Linux. The ustat manpage tells us since 1995, that
+ * fstat() should be used, but I'm too dumb to do so.
+ */
+
+#define BSD_MOUNTTEST 
+
+#endif /* linux */
+
+/******************************************************************
+ * Sony NewsOS
+ ******************************************************************
+ ***   ***  **      **  *****  ***    *****************************
+ ***    **  **  ******  *****  **  ********************************
+ ***  *  *  **    ****  ** **  ****  ******************************
+ ***  **    **  ******  *   *  ******  ****************************
+ ***  ***   **      **    *    ***    *****************************
+ ******************************************************************/
+#if defined(__sony_news) || defined(sony_news)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rsd/b0i6u0p2\0"
+
+#endif
+
+/******************************************************************
+ * OpenBSD
+ ******************************************************************
+ ****    ***     ***      **   ***  **     ****    ***    *********
+ ***  **  **  **  **  ******    **  **  **  **  ******  *  ********
+ ***  **  **     ***    ****  *  *  **    ******  ****  **  *******
+ ***  **  **  ******  ******  **    **  **  ******  **  *  ********
+ ****    ***  ******      **  ***   **     ****    ***    *********
+ ******************************************************************/
+#ifdef __OpenBSD__
+
+#define	DEFAULT_CD_DEVICE	"/dev/rcdrom"
+
+#endif
+
+/******************************************************************
+ * OSF1
+ ******************************************************************
+ ****    ****    ***      ***  ***  *******************************
+ ***  **  **  ******  ******  **    *******************************
+ ***  **  ****  ****    ***  *****  *******************************
+ ***  **  ******  **  ****  ******  *******************************
+ ****    ****    ***  ***  *****      *****************************
+ ******************************************************************/
+#if defined(__osf__)
+
+#define	DEFAULT_CD_DEVICE	"/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * SunOS/Solaris
+ ******************************************************************
+ ****    ***  **  **   ***  ***************************************
+ ***  ******  **  **    **  ***************************************
+ *****  ****  **  **  *  *  ***************************************
+ *******  **  **  **  **    ***************************************
+ ****    ****    ***  ***   ***************************************
+ ******************************************************************/
+#if defined(sun) || defined(__sun__)
+
+/*
+ * Define the following for Solaris 2.x
+ * If you don't want WorkMan to try to activate the SPARCstation 5
+ * internal audio input so you get sound from the workstation, comment
+ * out the CODEC define.
+ */
+
+#define SYSV 1
+#define CODEC 1
+
+/*
+ * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16"
+ * for PC (with SoundBlaster 16) running Solaris x86
+ * (only important if you define CODEC above)
+ */
+/*#define SUN_AUD_DEV "SUNW,CS4231"*/
+#define SUN_AUD_DEV "SUNW,sbpro"
+
+
+#endif
+
+/******************************************************************
+ * SVR4
+ ******************************************************************
+ ****    ***  ****  **     ***  *  ********************************
+ ***  ******  ****  **  **  **  *  ********************************
+ *****  *****  **  ***     ***      *******************************
+ *******  ***  **  ***  **  *****  ********************************
+ ****    *****    ****  **  *****  ********************************
+ ******************************************************************/
+#if defined(SVR4) && !defined(sun) && !defined(__sun__)
+
+#define DEFAULT_CD_DEVICE       "/dev/rcdrom/cd0"
+
+#endif
+
+/******************************************************************
+ * Ultrix
+ ******************************************************************
+ ***  **  **  *****      **     ***      **  **  ******************
+ ***  **  **  *******  ****  **  ****  *****    *******************
+ ***  **  **  *******  ****     *****  ******  ********************
+ ***  **  **  *******  ****  **  ****  *****    *******************
+ ****    ***      ***  ****  **  **      **  **  ******************
+ ******************************************************************/
+#if defined(ultrix) || defined(__ultrix)
+
+#endif
+
+/******************************************************************
+ * IBM AIX
+ ******************************************************************
+ ****    ***      **  **  *****************************************
+ ***  **  ****  *****    ******************************************
+ ***      ****  ******  *******************************************
+ ***  **  ****  *****    ******************************************
+ ***  **  **      **  **  *****************************************
+ ******************************************************************/
+#if defined(AIXV3)
+
+#define DEFAULT_CD_DEVICE	"/dev/cd0"
+
+#endif /* IBM AIX */
+
+/******************************************************************/ 
+#endif /* WM_CONFIG_H */
diff --git a/ascd/libworkman/include/wm_database.h b/ascd/libworkman/include/wm_database.h
new file mode 100644
index 0000000..aaea086
--- /dev/null
+++ b/ascd/libworkman/include/wm_database.h
@@ -0,0 +1,44 @@
+#ifndef WM_DATABASE_H
+#define WM_DATABASE_H
+/*
+ * $Id: wm_database.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes for WorkMan database
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+
+#define WM_DB_SAVE_ERROR	1
+#define WM_DB_SAVE_DISABLED	2
+ 
+int wm_db_get_playnew( void );
+void	split_workmandb( void );
+int	save( void );
+void	load( void );
+void	load_settings( void );
+
+extern int wm_db_save_disabled;
+extern int cur_playnew;
+
+#endif /* WM_DATABASE_H */
diff --git a/ascd/libworkman/include/wm_helpers.h b/ascd/libworkman/include/wm_helpers.h
new file mode 100644
index 0000000..0a3763b
--- /dev/null
+++ b/ascd/libworkman/include/wm_helpers.h
@@ -0,0 +1,105 @@
+#ifndef WM_HELPERS_H
+#define WM_HELPERS_H
+/*
+ * $Id: wm_helpers.h,v 1.6 1999/03/07 08:36:44 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Here to be found: Prototypes. Including variable names to be easier
+ * to read.
+ * This is just one more step to a more modular and understandable code.
+ *
+ */
+
+/*
+ * LibWorkMan message levels. I'm not sure how to call them all and which
+ * use they should fulfill. This is not very urgent now, because there
+ * aren't many messages in LibWorkMan now. 
+ */
+#define WM_MSG_LEVEL_NONE	0
+#define WM_MSG_LEVEL_ERROR	1
+#define WM_MSG_LEVEL_TWO	2
+#define WM_MSG_LEVEL_THREE	3
+#define WM_MSG_LEVEL_FOUR	4
+#define WM_MSG_LEVEL_INFO	5
+#define WM_MSG_LEVEL_SIX	6
+#define WM_MSG_LEVEL_SEVEN	7
+#define WM_MSG_LEVEL_EIGHT	8
+#define WM_MSG_LEVEL_DEBUG	9
+
+/*
+ * Message classes. This is somehow a definition of
+ * the message's source.
+ */
+ 
+#define WM_MSG_CLASS_PLATFORM	0x010
+#define WM_MSG_CLASS_SCSI	0x020
+#define WM_MSG_CLASS_CDROM      0x040
+#define WM_MSG_CLASS_DB		0x080
+#define WM_MSG_CLASS_MISC	0x100
+
+#define WM_MSG_CLASS_ALL	0xff0
+
+extern int wm_lib_verbosity;
+
+/*
+ * I did not know any better place...
+ */
+#ifdef DEBUG
+#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t );
+#else
+#define CHECKPOINT(t) 
+#endif
+
+#ifndef TRUE
+#define TRUE	1
+#endif
+
+#ifndef FALSE
+#define FALSE	0
+#endif
+
+#ifdef linux
+#include <signal.h>
+/* Linux doesn't have a SIGEMT */
+#if !defined( SIGEMT )
+#  define SIGEMT SIGUNUSED
+#endif
+#endif /* linux */
+
+void		freeup( char **x );
+void	 	wm_strmcat( char **t, char *s);
+void	 	wm_strmcpy( char **t, char *s );
+char *		wm_strdup( char *s );
+/* Somebody's version query unsatisfied? */
+int		wm_libver_major( void );	/* return major internal version number */
+int		wm_libver_minor( void );	/* return minor internal version number */
+int		wm_libver_pl( void );		/* return internal patchlevel number */
+char *		wm_libver_name( void );		/* return internal name (LibWorkMan) */
+char *		wm_libver_number( void );	/* returns string: "<major>.<minor>.<pl>" */
+char *		wm_libver_string( void ); 	/* returns string: "<name> <number>" */
+char *		wm_libver_date( void );		/* returns string: date of compilation */
+void 		wm_lib_set_verbosity( int level ); /* set verbosity level */
+int 		wm_lib_get_verbosity( void );      /* get verbosity level */
+void 		wm_lib_message( unsigned int level, char *format, ... ); /* put out a message on stderr */
+int		wm_susleep( int usec );
+
+#endif /* WM_HELPERS_H */
diff --git a/ascd/libworkman/include/wm_index.h b/ascd/libworkman/include/wm_index.h
new file mode 100644
index 0000000..ce14ac3
--- /dev/null
+++ b/ascd/libworkman/include/wm_index.h
@@ -0,0 +1,36 @@
+#ifndef WM_INDEX_H
+#define WM_INDEX_H
+/*
+ * $Id: wm_index.h,v 1.2 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Prototypes for wm_index.c
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+int	idx_find_entry( char *file, int ntracks, int *tracks,
+		 	int len, int fuzz, unsigned long *pos );
+int	idx_delete_entry(char *file, int track, int fuzz, unsigned long pos );
+int	idx_write_entry( char *file, int track, unsigned long pos );
+
+#endif /* WM_INDEX_H */
diff --git a/ascd/libworkman/include/wm_platform.h b/ascd/libworkman/include/wm_platform.h
new file mode 100644
index 0000000..03f957d
--- /dev/null
+++ b/ascd/libworkman/include/wm_platform.h
@@ -0,0 +1,51 @@
+#ifndef WM_PLATFORM_H
+#define WM_PLATFORM_H
+/*
+ * $Id: wm_platform.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * The platform interface
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h"
+
+int wmcd_open( struct wm_drive *d );
+int wmcd_reopen( struct wm_drive *d );
+
+/*
+ * void	keep_cd_open( void );
+ */
+int	wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+         	 void *retbuf, int retbuflen, int getreply );
+
+/****************************************
+ *
+ * The drive prototypes.
+ *
+ */
+extern struct wm_drive generic_proto;
+extern struct wm_drive sony_proto;
+extern struct wm_drive toshiba_proto;
+
+#endif /* WM_PLATFORM_H */
diff --git a/ascd/libworkman/include/wm_scsi.h b/ascd/libworkman/include/wm_scsi.h
new file mode 100644
index 0000000..8763a62
--- /dev/null
+++ b/ascd/libworkman/include/wm_scsi.h
@@ -0,0 +1,48 @@
+#ifndef WM_SCSI_H
+#define WM_SCSI_H
+/*
+ * $Id: wm_scsi.h,v 1.3 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * SCSI prototypes (scsi.c)
+ * 
+ * This is just one more step to a more modular and understandable code.
+ */
+
+#include "wm_struct.h" 
+
+#define	WM_ERR_SCSI_INQUIRY_FAILED -1
+
+int	wm_scsi_mode_sense( struct wm_drive *d, unsigned char page,
+			    unsigned char *buf );
+int	sendscsi( struct wm_drive *d, void *buf,
+		  unsigned int len, int dir,
+		  unsigned char a0, unsigned char a1,
+		  unsigned char a2, unsigned char a3,
+		  unsigned char a4, unsigned char a5,
+		  unsigned char a6, unsigned char a7,
+		  unsigned char a8, unsigned char a9,
+		  unsigned char a10, unsigned char a11 );
+ int	wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+				char *model, char *rev );
+
+#endif /* WM_SCSI_H */
diff --git a/ascd/libworkman/include/wm_struct.h b/ascd/libworkman/include/wm_struct.h
new file mode 100644
index 0000000..ae1b72a
--- /dev/null
+++ b/ascd/libworkman/include/wm_struct.h
@@ -0,0 +1,181 @@
+#ifndef WM_STRUCT_H
+#define WM_STRUCT_H
+/*
+ * $Id: wm_struct.h,v 1.7 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+
+/*
+ * Structure for a single track.  This is pretty much self-explanatory --
+ * one of these exists for each track on the current CD.
+ */
+struct wm_trackinfo 
+{
+  char	*songname;	/* Name of song, dynamically allocated */
+  char	*otherdb;	/* Unrecognized info for this track */
+  char	*otherrc;
+  int	length;		/* Length of track in seconds or Kbytes */
+  int	start;		/* Starting position (f+s*75+m*60*75) */
+  int	volume;		/* Per-track volume (1-32, 0 to disable) */
+  int	track;		/* Physical track number */
+  int	section;	/* Section number (0 if track not split) */
+  int	contd;		/* Flag: continuation of previous track */
+  int	avoid;		/* Flag: don't play this track. */
+  int	data;		/* Flag: data track */
+};
+
+/*
+ * Structure for internal playlist management.  The internal playlist is
+ * simply the list of track ranges that are being played currently.  This
+ * is built whenever the CD starts playing; it's used in normal and shuffle
+ * modes as well as playlist mode.
+ *
+ * The "starttime" element represents how much time has elapsed by the time
+ * we get to this entry.  For instance, if the list begins with a 5-minute
+ * track and a 3-minute track, the third entry would have a starttime of 8
+ * minutes.  This is used so that the elapsed play time can be displayed
+ * even in shuffle or playlist modes.
+ *
+ * The last member of the list has a start track of 0, and its starttime is
+ * the total playing time of the playlist (which will usually be overestimated,
+ * since we don't play leadouts in some cases.)
+ */
+struct wm_play 
+{
+  int	start;		/* Start track, or 0 if end of list */
+  int	end;		/* last track plus 1 */
+  int	starttime;	/* Number of seconds elapsed previously */
+};
+
+/*
+ * Structure for playlists (as seen by the user.)  This is simply a name
+ * followed by a zero-terminated list of track numbers to play.  The list
+ * is terminated by a NULL name.
+ */
+struct wm_playlist 
+{
+  char	*name;		/* Name of this playlist */
+  int	*list;		/* List of tracks */
+};
+
+struct wm_cdinfo
+{
+  char	artist[84];	/* Artist's name */
+  char	cdname[84];	/* Disc's name */
+  int	ntracks;	/* Number of tracks on the disc */
+  int	length;		/* Total running time in seconds */
+  int	autoplay;	/* Start playing CD immediately */
+  int	playmode;	/* How to play the CD */
+  int	volume;		/* Default volume (1-32, 0 for none) */
+  struct wm_trackinfo *trk;	/* struct wm_trackinfo[ntracks] */
+  struct wm_playlist *lists;	/* User-specified playlists */
+  char	*whichdb;	/* Which database is this entry from? */
+  char	*otherdb;	/* Unrecognized lines from this entry */
+  char	*otherrc;
+  char	*user;		/* Name of originating user */
+  unsigned int cddbid;  /* CDDB-ID of the current disc */
+  struct cdinfo *next;	/* For browsers, etc. */
+};
+
+/* The global variable "cd" points to the struct for the CD that's playing. */
+extern struct wm_cdinfo *cd;
+
+struct wm_playlist *new_list();
+
+enum wm_cd_modes	
+{
+  WM_CDM_UNKNOWN = -1,
+  WM_CDM_BACK = 0, WM_CDM_TRACK_DONE = 0,
+  WM_CDM_PLAYING = 1,
+  WM_CDM_FORWARD = 2,
+  WM_CDM_PAUSED = 3,
+  WM_CDM_STOPPED = 4,
+  WM_CDM_EJECTED = 5
+};
+
+/*
+ * Drive descriptor structure.  Used for access to low-level routines.
+ */
+struct wm_drive 
+{
+  int	fd;		/* File descriptor, if used by platform */
+  char	vendor[32];	/* Vendor name */
+  char	model[32];	/* Drive model */
+  char  revision[32];   /* Revision of the drive */
+  void	*aux;		/* Pointer to optional platform-specific info */
+  void	*daux;		/* Pointer to optional drive-specific info */
+  
+  int	(*init)();
+  int	(*get_trackcount)();
+  int	(*get_cdlen)();
+  int	(*get_trackinfo)();
+  int	(*get_drive_status)();
+  int	(*get_volume)();
+  int	(*set_volume)();
+  int	(*pause)();
+  int	(*resume)();
+  int	(*stop)();
+  int	(*play)();
+  int	(*eject)();
+  int   (*closetray)();
+};
+
+/*
+ * Structure for information of the usage of cddb.
+ */
+struct wm_cddb {
+        int     protocol;               /* 0-off, 1-cddbp, 2-http, 3-htproxy */
+        char    cddb_server[84];        /* host.domain.name:port */
+        char    mail_adress[84];        /* [email protected] */
+        char    path_to_cgi[84];        /* (/)path/to/cddb.cgi */
+        char    proxy_server[84];       /* host.domain.name:port */
+};
+extern struct wm_cddb cddb;
+
+
+/*
+ * Each platform has to define generic functions, so may as well declare
+ * them all here to save space.
+ * These functions should never be seen outside libworkman. So I don't care
+ * about the wm_ naming convention here.
+ */
+int     gen_init(),
+        gen_get_trackcount(),
+        gen_get_cdlen(),
+        gen_get_trackinfo(),
+	gen_get_drive_status(),
+	gen_get_volume(),
+	gen_set_volume(),
+	gen_pause(),
+	gen_resume(),
+	gen_stop(),
+	gen_play(),
+	gen_eject(),
+	gen_closetray();
+
+struct wm_drive *find_drive_struct();
+
+
+#endif /* WM_STRUCT_H */
diff --git a/ascd/libworkman/include/wm_version.h b/ascd/libworkman/include/wm_version.h
new file mode 100644
index 0000000..b18a2e2
--- /dev/null
+++ b/ascd/libworkman/include/wm_version.h
@@ -0,0 +1,35 @@
+#ifndef WM_VERSION_H
+#define WM_VERSION_H
+/*
+ * $Id: wm_version.h,v 1.2 1999/05/28 03:35:58 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Version information
+ *
+ */
+
+#define WM_LIBVER_MAJOR	1
+#define WM_LIBVER_MINOR 4
+#define WM_LIBVER_PL	2
+#define WM_LIBVER_NAME  "LibWorkMan"
+
+#endif /* WM_VERSION_H */
diff --git a/ascd/libworkman/include/workman.h b/ascd/libworkman/include/workman.h
new file mode 100644
index 0000000..2172b9f
--- /dev/null
+++ b/ascd/libworkman/include/workman.h
@@ -0,0 +1,51 @@
+#ifndef WORKMAN_H
+#define WORKMAN_H
+/*
+ * $Id: workman.h,v 1.5 1999/05/05 16:34:22 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * all-in-one libworkman include file.
+ *
+ */
+
+
+/*
+ * wm_config should always be included first
+ */
+#include "wm_config.h" 
+
+#include "workman_defs.h"
+#ifdef BUILD_CDDA
+#include "wm_cdda.h"
+#endif 
+#include "wm_cddb.h"
+#include "wm_cdinfo.h" 
+#include "wm_cdrom.h" 
+#include "wm_database.h" 
+#include "wm_helpers.h" 
+#include "wm_index.h" 
+#include "wm_platform.h" 
+#include "wm_scsi.h" 
+#include "wm_struct.h" 
+
+#endif /* WORKMAN_H */
+
diff --git a/ascd/libworkman/include/workman_defs.h b/ascd/libworkman/include/workman_defs.h
new file mode 100644
index 0000000..712a414
--- /dev/null
+++ b/ascd/libworkman/include/workman_defs.h
@@ -0,0 +1,30 @@
+#ifndef WORKMAN_DEFS_H
+#define WORKMAN_DEFS_H
+/*
+ * $Id: workman_defs.h,v 1.4 1999/02/14 09:50:47 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player program
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * 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
+ * (at your option) 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.
+ *
+ * #defined CONSTANTS
+ * 
+ * Too bad this file seems to be so empty...
+ *
+ */
+
+#include "wm_version.h"
+
+#endif /* WORKMAN_DEFS_H */
diff --git a/ascd/libworkman/index.c b/ascd/libworkman/index.c
new file mode 100644
index 0000000..e27cc0f
--- /dev/null
+++ b/ascd/libworkman/index.c
@@ -0,0 +1,385 @@
+/*
+ * $Id: index.c,v 1.2 1999/02/14 09:50:42 dirk Exp $
+ * 
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Maintain an external index file for a WorkMan CD database.
+ * Uses the Berkeley libdb library, available from ftp.cs.berkeley.edu.
+ */
+
+static char index_id[] = "$Id: index.c,v 1.2 1999/02/14 09:50:42 dirk Exp $";
+
+#ifdef LIBDB
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <db.h>
+#include <fcntl.h>
+#include <string.h>
+#include <netinet/in.h>	/* for htonl() */
+#include "include/wm_config.h"
+#include "include/wm_index.h"
+
+extern int suppress_locking;
+
+/*
+ * idx_find_entry()
+ *
+ * Find an entry in the index file.
+ *
+ * Input:
+ *	file	Name of database file (text version).
+ *	ntracks	Number of tracks on the CD we're looking for.
+ *	tracks	Array of track start times.
+ *	len	CD length in frames.
+ *	fuzz	Fuzz factor (tolerance value).
+ *	pos	Pointer to return value.
+ *
+ * Output:
+ *	1	No matching record found.
+ *	0	Record found; *pos contains offset into text file.
+ *	-1	Index file out of date or inaccessible, or another error.
+ */
+int
+idx_find_entry( char *file, int ntracks, int *tracks,
+		int len, int fuzz, unsigned long *pos )
+{
+	unsigned long	dbpos;
+	char		*indexname = NULL, keyval[8];
+	int		c;
+	FILE		*text;
+	DB		*index;
+	DBT		key, data;
+	BTREEINFO	bti;
+
+	/*
+	 * First, see if the text file is accessible.  Lock it if so.
+	 */
+	text = fopen(file, "r");
+	if (text == NULL)
+		return (-1);
+	if ((c = getc(text)) == EOF)
+	{
+		fclose(text);
+		return (-1);
+	}
+	if (! suppress_locking)
+		if (lockit(fileno(text), F_RDLCK))
+		{
+			fclose(text);
+			return (-1);
+		}
+
+	/*
+	 * Open the index file.
+	 */
+	indexname = malloc(strlen(file) + sizeof(".ind"));
+	if (indexname == NULL)
+	{
+		fclose(text);
+		return (-1);
+	}
+	strcpy(indexname, file);
+	strcat(indexname, ".ind");
+	bti.flags = 0;
+	bti.cachesize = 0;
+	bti.minkeypage = bti.maxkeypage = 0;
+	bti.psize = bti.lorder = 0;
+	bti.compare = NULL;
+	bti.prefix = NULL;
+	index = dbopen(indexname, O_RDONLY, 0666, DB_BTREE, &bti);
+	free(indexname);
+	if (index == NULL)
+	{
+		fclose(text);
+		return (-1);
+	}
+
+	/*
+	 * Search for the first matching entry.
+	 */
+	sprintf(keyval, "%07d", tracks[ntracks - 1] - fuzz);
+	key.data = keyval;
+	key.size = 7;
+	if (c = (index->seq)(index, &key, &data, R_CURSOR))
+	{
+		(index->close)(index);
+		fclose(text);
+		return (c);
+	}
+
+	/*
+	 * Now loop through all the possible matches, collecting them into
+	 * memory.
+	 */
+	do {
+		char	tracksline[750], *s;
+		int	i, val;
+
+		/* Hit the end of the valid entries? */
+		sscanf(key.data, "%d", &val);
+		if (val > tracks[ntracks - 1] + fuzz)
+			break;
+
+		dbpos = ntohl(*((unsigned long *) data.data));
+		if (fseek(text, dbpos, 0))
+			break;
+		
+		fgets(tracksline, sizeof(tracksline), text);
+		if (strncmp(tracksline, "tracks ", 7))
+			break;
+		(void) strtok(tracksline, " \t");
+
+		/* Got a valid tracks line.  See if it matches the CD. */
+		s = strtok(NULL, " \t");
+		if (s == NULL)
+			break;
+		if (atoi(s) != ntracks)
+			continue;
+		
+		for (i = 0; i < ntracks; i++)
+		{
+			s = strtok(NULL, " \t");
+			if (s == NULL)
+				break;
+			val = atoi(s);
+			if (val + fuzz < tracks[i] || val - fuzz > tracks[i])
+				break;
+		}
+		if (i != ntracks)
+			continue;
+
+		s = strtok(NULL, " \t");
+		if (s == NULL)
+			continue;
+		val = atoi(s);
+		if (val + fuzz / 75 < len / 75 || val + fuzz / 75 > len / 75)
+			continue;
+		
+		/* XXX - add to sorted list! */
+		*pos = dbpos;
+		(index->close)(index);
+		fclose(text);
+		return (0);
+	} while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+	if (c == 0)
+	{
+		/* An error. */
+		(index->close)(index);
+		fclose(text);
+		return (-1);
+	}
+
+	(index->close)(index);
+	fclose(text);
+	return (1);
+}
+
+/*
+ * idx_delete_entry()
+ *
+ * Delete an entry from the index file.
+ *
+ * Input:
+ *	file	Name of database file (text version).
+ *	track	Last track's start time (database key).
+ *	fuzz	Fuzz factor (tolerance value).
+ *	pos	Position of CD in database file.
+ *
+ * Output:
+ *	1	No matching record found.
+ *	0	Record deleted.
+ *	-1	Index file out of date or inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_delete_entry(char *file, int track, int fuzz, unsigned long pos )
+{
+	unsigned long	dbpos;
+	char		*indexname = NULL, keyval[8];
+	int		c, status;
+	DB		*index;
+	DBT		key, data;
+	BTREEINFO	bti;
+
+	/*
+	 * Open the index file.
+	 */
+	indexname = malloc(strlen(file) + sizeof(".ind"));
+	if (indexname == NULL)
+		return (-1);
+
+	strcpy(indexname, file);
+	strcat(indexname, ".ind");
+
+	bti.flags = 0;
+	bti.cachesize = 0;
+	bti.minkeypage = bti.maxkeypage = 0;
+	bti.psize = bti.lorder = 0;
+	bti.compare = NULL;
+	bti.prefix = NULL;
+	index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+	free(indexname);
+	if (index == NULL)
+		return (-1);
+
+	/*
+	 * Search for the first matching entry.
+	 */
+	sprintf(keyval, "%07d", track - fuzz);
+	key.data = keyval;
+	key.size = 7;
+	if (c = (index->seq)(index, &key, &data, R_CURSOR))
+	{
+		/*
+		 * Nothing matched!
+		 */
+		(index->close)(index);
+		return (c);
+	}
+
+	/*
+	 * Look for the entry the user wants to delete.
+	 */
+	do {
+		int	val;
+
+		/* Hit the end of the valid entries? */
+		sscanf(key.data, "%d", &val);
+		if (val > track + fuzz)
+			break;
+
+		/* Is this the entry we want? */
+		if (pos == ntohl(*((unsigned long *) data.data)))
+		{
+			/*
+			 * Yep!  Delete it.
+			 */
+			status = (index->del)(index, &key, R_CURSOR);
+			(index->close)(index);
+			return (status);
+		}
+	} while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0);
+
+	if (c == 0)
+	{
+		/* An error. */
+		(index->close)(index);
+		return (-1);
+	}
+
+	(index->close)(index);
+	return (1);
+}
+
+/*
+ * idx_write_entry()
+ *
+ * Write out an index file entry.
+ *
+ * Input:
+ *	file	Name of database file (text version).
+ *	track	Start time of last track (database key).
+ *	pos	Position of entry in text file.
+ *
+ * Output:
+ *	0	Record written.
+ *	-1	Index file inaccessible, or another error.
+ *
+ * Note: it is the caller's responsibility to do locking, as it's assumed
+ * that this operation will accompany a modification of the main database
+ * file and will need to be atomic with that modification.
+ */
+int
+idx_write_entry( char *file, int track, unsigned long pos )
+{
+	char		*indexname, keyval[8];
+	int		status;
+	DB		*index;
+	DBT		key, data;
+	BTREEINFO	bti;
+
+	/*
+	 * Open the index file.
+	 */
+	indexname = malloc(strlen(file) + sizeof(".ind"));
+	if (indexname == NULL)
+		return (-1);
+
+	strcpy(indexname, file);
+	strcat(indexname, ".ind");
+
+	bti.flags = R_DUP;
+	bti.cachesize = 0;
+	bti.minkeypage = 0;
+	bti.maxkeypage = 0;
+	bti.psize = 0;
+	bti.lorder = 4321;	/* network byte order */
+	bti.compare = NULL;
+	bti.prefix = NULL;
+	index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti);
+	free(indexname);
+	if (index == NULL)
+		return (-1);
+
+	/*
+	 * Create a new key and value.
+	 */
+	pos = htonl(pos);
+	data.data = &pos;
+	data.size = sizeof(pos);
+	key.data = keyval;
+	key.size = 7;
+
+	sprintf(keyval, "%07d", track);
+
+	status = (index->put)(index, &key, &data, 0);
+
+	(index->close)(index);
+	return (status);
+}
+
+#else /* LIBDB */
+
+int
+idx_find_entry()
+{
+	return (1);	/* no record found; text file will be searched. */
+}
+
+int
+idx_delete_entry()
+{
+	return (0);
+}
+
+int
+idx_write_entry()
+{
+	return (0);
+}
+
+#endif /* LIBDB */
diff --git a/ascd/libworkman/plat_aix.c b/ascd/libworkman/plat_aix.c
new file mode 100644
index 0000000..980bb50
--- /dev/null
+++ b/ascd/libworkman/plat_aix.c
@@ -0,0 +1,443 @@
+/*
+ * $Id: plat_aix.c,v 1.7 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * plat_aix - AIX 4.x IDE and SCSI support  16 Dec 1998
+ *
+ * AIX 4.x Port: Erik O'Shaughnessy 
+ * Original AIX IDE Code: Cloyce Spradling (xmcd libdi_d/aixioc.c )
+ *
+ * Taken from the ascd distribution.
+ *
+ */
+
+
+#ifdef AIXV3
+
+static char plat_aix_id[] = "$Id: plat_aix.c,v 1.7 1999/03/07 08:36:40 dirk Exp $";
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/cdrom.h>
+#include <sys/devinfo.h>
+#include <sys/scsi.h>
+#include <sys/scdisk.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define LEADOUT 0xaa
+
+extern char *cd_device;
+
+int min_volume = 128;
+int max_volume = 255;
+
+
+
+/* NAME: gen_init
+ *
+ * FUNCTION:
+ *
+ * RETURNS: 
+ */
+
+int gen_init(struct wm_drive *d){
+
+  return 0;
+}
+
+/* NAME: gen_get_trackcount
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_get_trackcount(struct wm_drive *d,int *tracks){ 
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_TRK_INFO_AUDIO;
+  cmd.msf_flag = 0;
+
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0){
+    perror("DKAUDIO");
+    return -1;
+  }
+
+  *tracks = cmd.indexing.track_index.last_track;
+
+  return 0;
+}
+
+
+/* NAME: gen_get_cdlen
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_get_cdlen(struct wm_drive *d,int *frames){ 
+  int tmp;
+
+  return gen_get_trackinfo(d,LEADOUT,&tmp,frames);
+}
+
+/* NAME: gen_get_trackinfo
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe){
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_GET_TRK_MSF;
+  cmd.msf_flag = 1;
+
+  cmd.indexing.track_msf.track = track;
+  
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+    return -1;
+  
+  *startframe = cmd.indexing.track_msf.mins * 60 * 75 +
+                cmd.indexing.track_msf.secs * 75 +
+                cmd.indexing.track_msf.frames;
+
+  *data = 0;
+
+  return 0;
+}
+
+/* NAME: gen_get_drive_status
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_get_drive_status(struct wm_drive *d,
+			 enum wm_cd_modes oldmode,
+			 enum wm_cd_modes *mode,
+			 int *pos,
+			 int *track,
+			 int *index){ 
+  struct cd_audio_cmd cmd;
+
+  *mode = WM_CDM_EJECTED;
+
+  if(d->fd < 0)
+    switch( wmcd_open(d) ){
+      case -1:
+	return -1;
+      case 1:
+	return 0;
+    }
+
+  cmd.audio_cmds = CD_INFO_AUDIO;
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+    return -1;
+
+  switch(cmd.status){
+    case CD_PLAY_AUDIO:
+      *mode = WM_CDM_PLAYING;
+      *track = cmd.indexing.info_audio.current_track;
+      *index = cmd.indexing.info_audio.current_index;
+      *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+	     cmd.indexing.info_audio.current_secs * 75 +
+	     cmd.indexing.info_audio.current_frames;
+      break;
+
+    case CD_PAUSE_AUDIO:
+      *mode = WM_CDM_PAUSED;
+      *track = cmd.indexing.info_audio.current_track;
+      *index = cmd.indexing.info_audio.current_index;
+      *pos = cmd.indexing.info_audio.current_mins * 60 * 75 +
+	     cmd.indexing.info_audio.current_secs * 75 +
+	     cmd.indexing.info_audio.current_frames;
+
+      break;
+    case CD_NO_AUDIO:		/* no play audio in progress */
+    case CD_COMPLETED:		/* play operation completed successfully */
+    case CD_STATUS_ERROR:	/* invalid status or play stopped due to err */
+    case CD_NOT_VALID:		/* audio status is invalid or not supported */
+      *mode = WM_CDM_STOPPED;
+      break;
+    default:
+      *mode = WM_CDM_UNKNOWN;
+      break;
+  }
+
+  return 0;
+}
+
+
+int scale_volume(int vol,int max){
+  return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+int unscale_volume(int vol,int max){
+  int n;
+  n = ( vol - min_volume ) * max_volume / (max - min_volume);
+  return (n <0)?0:n;
+}
+
+/* NAME: gen_get_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_get_volume(struct wm_drive *d,int *left,int *right){ 
+  struct cd_audio_cmd cmd;
+  int l,r;
+
+  fprintf(stderr,"gen_get_volume\n");
+
+  cmd.audio_cmds = CD_INFO_AUDIO;
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0)
+    return -1;
+
+  *left = unscale_volume(cmd.out_port_0_vol,100);
+  *right = unscale_volume(cmd.out_port_1_vol,100);
+
+  return 0;
+}
+
+/* NAME: gen_set_volume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_set_volume(struct wm_drive *d,int left,int right){ 
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_SET_VOLUME;
+  cmd.volume_type = CD_VOLUME_CHNLS;
+  
+  cmd.out_port_0_vol = scale_volume(left,100);
+  cmd.out_port_1_vol = scale_volume(right,100);
+
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0){
+    perror("CD_SET_VOLUME");
+    return -1;
+  }
+
+  return 0;
+}
+
+/* NAME: gen_pause
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_pause(struct wm_drive *d){ 
+  struct cd_audio_cmd cmd;
+  
+  cmd.audio_cmds = CD_PAUSE_AUDIO;
+
+  return ioctl(d->fd,DKAUDIO,&cmd);
+}
+
+/* NAME: gen_resume
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_resume(struct wm_drive *d){ 
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_RESUME_AUDIO;
+  return ioctl(d->fd,DKAUDIO,&cmd);
+}
+
+/* NAME: gen_stop
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_stop(struct wm_drive *d){ 
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_STOP_AUDIO;
+  return ioctl(d->fd,DKAUDIO,&cmd);
+}
+
+/* NAME: gen_play
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_play(struct wm_drive *d,int start,int end){ 
+  struct cd_audio_cmd cmd;
+
+  cmd.audio_cmds = CD_PLAY_AUDIO;
+  cmd.msf_flag = 1;
+
+  cmd.indexing.msf.first_mins = start / (60*75);
+  cmd.indexing.msf.first_secs = (start % (60*75)) / 75;
+  cmd.indexing.msf.first_frames = start % 75;
+
+  cmd.indexing.msf.last_mins = end / (60*75);
+  cmd.indexing.msf.last_secs = (end % (60*75)) / 75;
+  cmd.indexing.msf.last_frames = end % 75;
+
+  if( ioctl(d->fd,DKAUDIO,&cmd) < 0){
+    perror("DKAUDIO:CD_PLAY_AUDIO");
+    return -1;
+  }
+
+  return 0;
+}
+
+/* NAME: gen_eject
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int gen_eject(struct wm_drive *d){ 
+
+  return ioctl(d->fd,DKEJECT,NULL);
+}
+
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/* NAME: wm_scsi
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+
+
+int wm_scsi(struct wm_drive *d,
+	    uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply){
+  return 0;
+}
+
+
+/* NAME: wmcd_open
+ *
+ * FUNCTION:
+ *
+ * RETURNS:
+ */
+
+int wmcd_open(struct wm_drive *d){
+  char vendor[32] = WM_STR_GENVENDOR;
+  char  model[32] = WM_STR_GENMODEL;
+  char    rev[32] = WM_STR_GENREV;
+
+  int fd;
+
+  if( ! d ){
+    errno = EFAULT;
+    return -1;
+  }
+
+  if(d->fd > -1)			/* device already open? */
+    return 0;
+
+  if( cd_device == (char *)NULL )
+    cd_device = DEFAULT_CD_DEVICE;
+
+  if( (fd=openx(cd_device,O_RDONLY,NULL,SC_SINGLE)) < 0 ){
+    perror("openx");
+    return 1;
+  }
+
+  *d = *(find_drive_struct(vendor, model, rev));
+  wm_drive_settype(vendor, model, rev);
+  
+  d->fd = fd;
+  d->init(d);
+
+  return 0;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif /* _AIX */
diff --git a/ascd/libworkman/plat_bsd386.c b/ascd/libworkman/plat_bsd386.c
new file mode 100644
index 0000000..9a58e84
--- /dev/null
+++ b/ascd/libworkman/plat_bsd386.c
@@ -0,0 +1,500 @@
+/*
+ * $Id: plat_bsd386.c,v 1.5 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * BSD/386-specific drive control routines.
+ */
+
+#ifdef __bsdi__
+
+static char plat_bsd386_id[] = "$Id: plat_bsd386.c,v 1.5 1999/03/07 08:36:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * The following is included from the Linux module. However, I didn't
+ * see a check here if the CD to be ejected is mounted...
+ */
+#if defined(BSD_MOUNTTEST)
+  #include <mntent.h>
+#else
+  /*
+   * this is for glibc 2.x which defines ust structure in
+   * ustat.h not stat.h. 
+   */
+  #ifdef __GLIBC__
+    #include <sys/ustat.h>
+  #endif
+#endif
+
+
+#include <sys/time.h>
+#include <string.h>
+#include <sys/cdrom.h>
+#ifdef SOUNDBLASTER
+# include <i386/isa/sblast.h>
+#endif
+
+#include "include/wm_struct.h"
+
+/*
+ * Since we can't sense the drive type with libcdrom anyway, and since the
+ * library doesn't provide "pause" or "resume" functions, use the daux field
+ * to point to the frame number at which we paused.
+ */
+struct pause_info {
+	int	frame;
+	int	endframe;
+};
+#define	PAUSE_FRAME	(((struct pause_info *) d->daux)->frame)
+#define	END_FRAME	(((struct pause_info *) d->daux)->endframe)
+#define CUR_CD		((struct cdinfo *) d->aux)
+
+void *malloc();
+
+extern char	*cd_device;
+
+#ifdef SOUNDBLASTER
+	int	min_volume = 0;
+	int	max_volume = 15;
+	int	min_volume_drive = 10;	/* Toshiba drive does low values. */
+	int	max_volume_drive = 255;
+#else /* not SOUNDBLASTER, libcdrom only */
+	int	min_volume = 10;
+	int	max_volume = 255;
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive.  A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+	*tracks = CUR_CD->ntracks;
+
+	return (0);
+}
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+	*data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0;
+	*startframe = CUR_CD->tracks[track - 1].start_frame;
+
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+	*frames = CUR_CD->total_frames;
+
+	return (0);
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status(struct wm_drive *d, enum wm_cd_modes oldmode,
+                     enum wm_cd_modes *mode, int *pos, int *track, int *index)
+{
+	struct cdstatus	status;
+	extern enum wm_cd_modes cur_cdmode;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	/* Is the device open? */
+	if (d->aux == NULL)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	if (cdstatus (CUR_CD, &status) < 0)
+	{
+		*mode = WM_CDM_TRACK_DONE;	/* waiting for next track. */
+		return (0);
+	}
+
+#define DOPOS \
+		*pos = status.abs_frame; \
+		*track = status.track_num; \
+		*index = status.index_num
+
+	switch (status.state) {
+	case cdstate_playing:
+		*mode = WM_CDM_PLAYING;
+		DOPOS;
+		break;
+
+	case cdstate_stopped:
+		/* the MITSUMI drive doesn't have a "paused" state,
+		   so it always comes here and not to the paused section.
+		   The PAUSE_FRAME stuff below (in gen_pause())
+		   fakes out the paused state. */
+		if (oldmode == WM_CDM_PLAYING) {
+		    *mode = WM_CDM_TRACK_DONE;
+		    break;
+		} else if (cur_cdmode != WM_CDM_PAUSED) {
+		    *mode = WM_CDM_STOPPED;
+		    DOPOS;
+		    break;
+		}
+		/* fall through if paused */
+
+	case cdstate_paused:
+		/* the SCSI2 code in the cdrom library only pauses with
+		   cdstop(); it never truly stops a disc (until an in-progress
+		   play reaches the end).  So it always comes here. */
+		if (cur_cdmode == WM_CDM_STOPPED) {
+		    *mode = WM_CDM_STOPPED;
+		    DOPOS;
+		    break;
+		}
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) {
+			*mode = WM_CDM_PAUSED;
+			DOPOS;
+		} else {
+		    *mode = WM_CDM_STOPPED;
+		    DOPOS;
+		}
+		break;
+
+	default:
+		*mode = WM_CDM_STOPPED;
+	}
+
+	return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+	/* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */
+	return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+#ifdef SOUNDBLASTER
+	/* Send a Mixer IOCTL */
+	if (d->fd >= 0) {
+		struct sb_mixer_levels levels;
+		if (ioctl(d->fd, MIXER_IOCTL_READ_LEVELS, &levels) == 0) {
+			levels.cd.l = left < 0 ? 0 : left;
+			levels.cd.r = right < 0 ? 0 : right;
+			(void) ioctl(d->fd, MIXER_IOCTL_SET_LEVELS, &levels);
+		} else
+			perror("SoundBlaster mixer read failed");
+	} else
+#endif
+	/* NOTE: the cdvolume2() call is an addition to the cdrom library.
+	   Pick it up from the archives on bsdi.com */
+	cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left,
+		   right < 0 ? 0 : right > 255 ? 255 : right);
+
+	return (0);
+}
+
+/*--------------------------------------------------------------------*
+ * Pause the CD.  This is a bit of a trick since there's no cdpause()
+ * function in the library.  We fake it by saving the frame number
+ * and stopping.
+ *--------------------------------------------------------------------*/
+int
+gen_pause(struct wm_drive *d)
+{
+	struct cdstatus	status;
+
+	if (cdstatus(d->aux, &status) < 0)
+		return (-1);
+	if (status.state != cdstate_playing)
+		PAUSE_FRAME = CUR_CD->tracks[0].start_frame;
+	else
+		PAUSE_FRAME = status.abs_frame;
+	if (cdstop(d->aux) < 0)
+		return (-1);
+
+	return (0);
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+	int	status;
+
+	status = (d->play)(d, PAUSE_FRAME, END_FRAME);
+	PAUSE_FRAME = 0;
+	return (status);
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+    return cdstop(d->aux);
+}
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+	END_FRAME = end;
+	if (cdplay(d->aux, start, end) < 0)
+		return (-1);
+	else
+		return (0);
+}
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+	cdeject(d->aux);
+	cdclose(d->aux);
+	d->aux = NULL;
+	free(d->daux);
+	d->daux = NULL;
+
+	return (0);
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_open(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume(int cd_vol, int max)
+{
+	int	vol = 0, top = max, bot = 0, scaled;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol == scaled)
+			break;
+		if (cd_vol < scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+	/* Most systems can't seem to do this... */
+	*left = *right = -1;
+
+#ifdef SOUNDBLASTER
+	/* Send a Mixer IOCTL */
+	if (d->fd >= 0) {
+	    struct sb_mixer_levels levels;
+	    if (ioctl(d->fd, MIXER_IOCTL_READ_LEVELS, &levels) == 0) {
+		*left = unscale_volume(levels.cd.l, 100);
+		*right = unscale_volume(levels.cd.r, 100);
+	    } else
+		perror("SoundBlaster mixer read failed");
+	}
+#endif
+
+	return (0);
+}
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen,
+        void *retbuf, int retbuflen, int getreply)
+{
+	/* Don't know how to do SCSI passthrough... */
+	return (-1);
+}
+
+#ifdef SOUNDBLASTER
+int	sb_fd = -3;			/* patchable from external programs */
+#endif
+
+/*-----------------------------------------------------------------------*
+ * Open the CD device.  We can't determine the drive type under BSD/386.
+ *-----------------------------------------------------------------------*/
+int
+wmcd_open(struct wm_drvie *d)
+{
+	void	*aux = NULL, *daux = NULL;
+	int fd = -1;
+
+	if (d->aux)	/* Device already open? */
+		return (0);
+
+	if ((aux = cdopen(cd_device)) == NULL)
+	{
+		fprintf(stderr, "No cdrom found by libcdrom\n");
+		exit(1);
+	}
+
+	if ((daux = malloc(sizeof(struct pause_info))) == NULL)
+		return (-1);
+
+#ifdef SOUNDBLASTER
+	if (sb_fd != -3 && sb_fd < 0)
+	    fd = open ("/dev/sb_mixer", O_RDWR, 0);
+	if (fd < 0) {
+	    if (sb_fd != -3)
+		fprintf (stderr,"SoundBlaster mixer not found/disabled; will use direct CD volume control\n");
+	    max_volume = max_volume_drive;
+	    min_volume = min_volume_drive;
+	}
+#endif
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	*d = *(find_drive_struct("", "", ""));
+	d->aux = aux;
+	d->daux = daux;
+	d->fd = fd;
+	PAUSE_FRAME = 0;
+	END_FRAME = 0;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+#endif /* __bsdi__ */
diff --git a/ascd/libworkman/plat_freebsd.c b/ascd/libworkman/plat_freebsd.c
new file mode 100644
index 0000000..945b316
--- /dev/null
+++ b/ascd/libworkman/plat_freebsd.c
@@ -0,0 +1,565 @@
+/*
+ * $Id: plat_freebsd.c,v 1.8 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * plat_freebsd.c
+ *
+ * FreeBSD-specific drive control routines.
+ *
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+
+static char freebsd_id[] = "$Id: plat_freebsd.c,v 1.8 1999/03/07 08:36:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/scsiio.h>
+
+#if defined(__NetBSD__)
+# define MSF_MINUTES 1
+# define MSF_SECONDS 2
+# define MSF_FRAMES 3
+# include "/sys/scsi/scsi_all.h"
+# include "/sys/scsi/scsi_cd.h"
+#else
+# define LEFT_PORT 0
+# define RIGHT_PORT 1
+# if __FreeBSD_version < 300000
+#  include <scsi.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int	min_volume = 10;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+
+/*--------------------------------------------------------*
+ * Initialize the drive.  A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init(struct wm_drive *d)
+{
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+	struct ioc_toc_header	hdr;
+
+	if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+		return (-1);
+
+	*tracks = hdr.ending_track - hdr.starting_track + 1;
+
+	return (0);
+}
+
+/*-----------------------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ *-----------------------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+	struct ioc_read_toc_entry	toc;
+	struct cd_toc_entry		toc_buffer;
+
+	bzero((char *)&toc_buffer, sizeof(toc_buffer));
+	toc.address_format = CD_MSF_FORMAT;
+	toc.starting_track = track;
+	toc.data_len = sizeof(toc_buffer);
+	toc.data = &toc_buffer;
+
+	if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+		return (-1);
+
+	*data = ((toc_buffer.control & 0x4) != 0);
+
+#ifdef __NetBSD__
+	*startframe = toc_buffer.addr[MSF_MINUTES]*60*75 +
+		toc_buffer.addr[MSF_SECONDS] * 75 +
+		toc_buffer.addr[MSF_FRAMES];
+#else
+	*startframe = toc_buffer.addr.msf.minute*60*75 +
+		toc_buffer.addr.msf.second * 75 +
+		toc_buffer.addr.msf.frame;
+#endif
+
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+	int		tmp;
+	struct ioc_toc_header		hdr;
+	int status;
+
+#define LEADOUT 0xaa			/* see scsi.c.  what a hack! */
+	return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status(struct wm_drive *d, enum wm_cd_modes oldmode,
+                     enum wm_cd_modes *mode, int *pos, int *track, int *index)
+{
+	struct ioc_read_subchannel	sc;
+	struct cd_sub_channel_info	scd;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	sc.address_format	= CD_MSF_FORMAT;
+	sc.data_format		= CD_CURRENT_POSITION;
+	sc.track		= 0;
+	sc.data_len		= sizeof(scd);
+	sc.data			= (struct cd_sub_channel_info *)&scd;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc)) {
+/*
+ * #ifdef __NetBSD__
+ *
+ * Denis Bourez <[email protected]> told me, that closing the
+ * device is mandatory for FreeBSD, too.
+ */
+	    /* we need to release the device so the kernel will notice
+	       reloaded media */
+	    (void) close(d->fd);
+	    d->fd = -1;
+/*
+ * #endif
+ */
+		return (0);	/* ejected */
+	}
+
+	switch (scd.header.audio_status) {
+	case CD_AS_PLAY_IN_PROGRESS:
+		*mode = WM_CDM_PLAYING;
+dopos:
+#ifdef __NetBSD__
+		*pos = scd.what.position.absaddr[MSF_MINUTES] * 60 * 75 +
+			scd.what.position.absaddr[MSF_SECONDS] * 75 +
+			scd.what.position.absaddr[MSF_FRAMES];
+#else
+		*pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+			scd.what.position.absaddr.msf.second * 75 +
+			scd.what.position.absaddr.msf.frame;
+#endif
+		*track = scd.what.position.track_number;
+		*index = scd.what.position.index_number;
+		break;
+
+	case CD_AS_PLAY_PAUSED:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			goto dopos;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case CD_AS_PLAY_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	case CD_AS_NO_STATUS:
+	case 0:
+		*mode = WM_CDM_STOPPED;
+		break;
+	}
+
+	return (0);
+}
+
+/*---------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom.  To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting.  The actual formula looks like
+ *
+ *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ *                           max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ *	return ((vol * (max_volume - min_volume)) / max + min_volume);
+ *---------------------------------------------------------------------------*/
+static int
+scale_volume(int vol, int max)
+{
+    return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+	struct ioc_vol vol;
+
+	if (left < 0)	/* don't laugh, I saw this happen once! */
+	    left = 0;
+	if (right < 0)
+	    right = 0;
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+	bzero((char *)&vol, sizeof(vol));
+
+	vol.vol[LEFT_PORT] = left;
+	vol.vol[RIGHT_PORT] = right;
+
+	if (ioctl(d->fd, CDIOCSETVOL, &vol))
+		return (-1);
+
+	return (0);
+}
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+	return (ioctl(d->fd, CDIOCPAUSE));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+	return (ioctl(d->fd, CDIOCRESUME));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDIOCSTOP));
+}
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end )
+{
+	struct ioc_play_msf	msf;
+
+	msf.start_m	= start / (60*75);
+	msf.start_s	= (start % (60*75)) / 75;
+	msf.start_f	= start % 75;
+	msf.end_m	= end / (60*75);
+	msf.end_s	= (end % (60*75)) / 75;
+	msf.end_f	= end % 75;
+
+	if (ioctl(d->fd, CDIOCSTART))
+		return (-1);
+
+	if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+		return (-2);
+
+	return (0);
+}
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+	/* On some systems, we can check to see if the CD is mounted. */
+	struct stat	stbuf;
+	struct statfs	buf;
+	int rval;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (fstatfs(stbuf.st_rdev, &buf) == 0)
+		return (-3);
+
+	rval = ioctl(d->fd, CDIOCALLOW);
+   
+	if (rval == 0)
+	    rval = ioctl(d->fd, CDIOCEJECT);
+   
+	if (rval == 0)
+	    rval = ioctl(d->fd, CDIOCPREVENT);
+   
+	(void) close(d->fd);
+   
+	return rval;
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*---------------------------------------------------------------------------*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ *--------------------------------------------------------------------------*/
+static int
+unscale_volume( int cd_vol, int max )
+{
+	int	vol = 0, top = max, bot = 0, scaled;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol == scaled)
+			break;
+		if (cd_vol < scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+	struct ioc_vol vol;
+
+	if (d->fd >= 0)
+	{
+		bzero((char *)&vol, sizeof(vol));
+
+		if (ioctl(d->fd, CDIOCGETVOL, &vol))
+			*left = *right = -1;
+		else
+		{
+			*left = unscale_volume(vol.vol[LEFT_PORT], 100);
+			*right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+		}
+	}
+	else
+		*left = *right = -1;
+
+	return (0);
+}
+
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *
+ *---------------------------------------------*/
+int
+wm_scsi(d, cdb, cdblen, retbuf, retbuflen, getreply)
+	struct wm_drive	*d;
+	unsigned char	*cdb;
+	int		cdblen;
+	void		*retbuf;
+	int		retbuflen;
+	int		getreply;
+{
+	return (-1);
+}
+
+
+/*-------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *-------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+	int		fd;
+	static int	warned = 0;
+	char vendor[32] = WM_STR_GENVENDOR;
+	char  model[32] = WM_STR_GENMODEL;
+	char    rev[32] = WM_STR_GENREV;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	d->fd = open(cd_device, 0);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+	*d = *(find_drive_struct(vendor, model, rev));
+	wm_drive_settype(vendor, model, rev);
+	
+	(d->init)(d);
+
+	d->fd = fd;
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif
diff --git a/ascd/libworkman/plat_hpux.c b/ascd/libworkman/plat_hpux.c
new file mode 100644
index 0000000..0175401
--- /dev/null
+++ b/ascd/libworkman/plat_hpux.c
@@ -0,0 +1,355 @@
+/*
+ * $Id: plat_hpux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * HP/UX-specific drive control routines.
+ */
+
+#if defined(hpux) || defined(__hpux)
+
+static char plat_hpux_id[] = "$Id: plat_hpux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/*
+ * this is for glibc 2.x which the ust structure in
+ * ustat.h not stat.h
+ */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <sys/scsi.h>
+
+#include "include/wm_struct.h"
+#include "include/wm_helpers.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int	min_volume = 0;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+/*--------------------------------------------------------*
+ * Initialize the drive.  A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks )
+{
+	return (wm_scsi2_get_trackcount(d, tracks));
+}
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int *track, int *data, int *startframe)
+{
+	return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+	int		tmp;
+
+	return (wm_scsi2_get_cdlen(d, frames));
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, enum wm_cd_modes oldmode, enum wm_cd_modes *mode,
+                      int *pos, int *track, int *index )
+	struct wm_drive	*d;
+	enum wm_cd_modes oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+}
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+	return (wm_scsi2_set_volume(d, left, right));
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+	return (wm_scsi2_get_volume(d, left, right));
+}
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+	return (wm_scsi2_pause(d));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+	return (wm_scsi2_resume(d));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+	return (wm_scsi2_stop(d));
+}
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+	return (wm_scsi2_play(d, start, end));
+}
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+	struct stat	stbuf;
+	struct ustat	ust;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+
+	return (wm_scsi2_eject(d));
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	return (wm_scsi2_closetray(d));
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+	 void *retbuf, int retbuflen, int getreply )
+{
+#ifdef SIOC_IO
+	struct sctl_io		cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.cdb_length = cdblen;
+	cmd.data = retbuf;
+	cmd.data_length = retbuflen;
+	cmd.max_msecs = 1000;
+	cmd.flags = getreply ? SCTL_READ : 0;
+	memcpy(cmd.cdb, cdb, cdblen);
+
+	return (ioctl(d->fd, SIOC_IO, &cmd));
+#else
+	/* this code, for pre-9.0, is BROKEN!  ugh. */
+	char			reply_buf[12];
+	struct scsi_cmd_parms	cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.clock_ticks = 500;
+	cmd.cmd_mode = 1;
+	cmd.cmd_type = cdblen;
+	memcpy(cmd.command, cdb, cdblen);
+	if (ioctl(d->fd, SIOC_SET_CMD, &cmd) < 0)
+		return (-1);
+
+	if (! retbuf || ! retbuflen)
+		(void) read(d->fd, reply_buf, sizeof(reply_buf));
+	else if (getreply)
+	{
+		if (read(d->fd, retbuf, retbuflen) < 0)
+			return (-1);
+	}
+	else
+		if (write(d->fd, retbuf, retbuflen) < 0)
+			return (-1);
+
+	return (0);
+#endif
+}
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+	int		fd, flag = 1;
+	static int	warned = 0;
+/* unsigned ? */
+	char		vendor[32], model[32], rev[32];
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+	
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	d->fd = open(cd_device, O_RDWR);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (! warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+		else if (errno != EINTR)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	/* Prepare the device to receive raw SCSI commands. */
+	if (ioctl(d->fd, SIOC_CMD_MODE, &flag) < 0)
+	{
+		fprintf(stderr, "%s: SIOC_CMD_MODE: true: %s\n",
+			cd_device, strerror(errno));
+		/*exit(1);*/
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+	/* Default drive is the HP one, which might not respond to INQUIRY */
+	strcpy(&vendor, "TOSHIBA");
+	strcpy(&model, "XM-3301");
+	rev[0] = '\0';
+	wm_scsi_get_drive_type(d, vendor, model, rev);
+	*d = *(find_drive_struct(vendor, model, rev));
+	wm_drive_settype(vendor, model, rev);
+	
+	d->fd = fd;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+
+#endif
diff --git a/ascd/libworkman/plat_irix.c b/ascd/libworkman/plat_irix.c
new file mode 100644
index 0000000..9019588
--- /dev/null
+++ b/ascd/libworkman/plat_irix.c
@@ -0,0 +1,456 @@
+/*
+ * $Id: plat_irix.c,v 1.6 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * IRIX specific.
+ *
+ * Taken from the kscd distribution
+ *
+ * Paul Kendall
+ * [email protected], or
+ * [email protected]
+ */
+
+#ifdef sgi
+ 
+static char plat_irix_id[] = "$Id: plat_irix.c,v 1.6 1999/03/07 08:36:40 dirk Exp $";
+
+#include "include/wm_config.h"
+
+/*
+ * Yes, it was designed for WorkMan 1.4b3
+ * Because I did start over from 1.3a, I disable it here.
+ * There is no guarantee of getting working code by defining
+ * CDDA yourself.
+ *
+ */
+#undef CDDA
+/*#define CDDA*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sigfpe.h>
+#include <dmedia/cdaudio.h>
+#include <dmedia/audio.h>
+#include <errno.h>
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+int	min_volume = 0;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+#ifdef CDDA
+static int playing = STOPPED;
+static CDPLAYER *icd;
+static CDPARSER *icdp;
+static CDFRAME cdbuf[12];
+static ALport audioport;
+static ALconfig aconfig;
+static struct itimerval audiotimer = { {0,0}, {0,25000} };
+static int cdtrack=0;
+static int cdframe=0;
+static int cdstopframe=0;
+
+cbprognum(void *arg, CDDATATYPES type, CDPROGNUM* prognum)
+{
+	cdtrack = prognum->value;
+}
+
+cbabstime(void *arg, CDDATATYPES type, struct cdtimecode* atime)
+{
+	cdframe = CDtctoframe(atime);
+	if( cdframe == cdstopframe )
+		playing = STOPPED;
+}
+
+cbplayaudio(void *arg, CDDATATYPES type, short* audio)
+{
+	if(playing != PLAYING) return;
+	ALwritesamps(audioport, audio, CDDA_NUMSAMPLES);
+}
+
+static void alarmsignal()
+{
+	int n, i;
+	if(playing != PLAYING) return;
+	if( ALgetfilled(audioport) < CDDA_NUMSAMPLES*8 ) {
+		/* Only get more samples and play them if we're getting low
+		 * this ensures that the CD stays close to the sound
+		 */
+		n = CDreadda(icd, cdbuf, 12);
+		if( n == 0 ) return;
+		for( i=0 ; i<12 ; i++ )
+			CDparseframe(icdp, &cdbuf[i]);
+	}
+	signal(SIGALRM, alarmsignal);
+	setitimer(ITIMER_REAL, &audiotimer, NULL);
+}
+#endif
+
+/*--------------------------------------------------------*
+ * Initialize the drive.  A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+#ifdef CDDA
+	long Param[4];
+	/* Set the audio rate to 44100Hz 16bit 2s-comp stereo */
+	aconfig = ALnewconfig();
+	ALsetwidth(aconfig, AL_SAMPLE_16);
+	ALsetsampfmt(aconfig, AL_SAMPFMT_TWOSCOMP);
+	ALsetchannels(aconfig, 2);
+	Param[0] = AL_OUTPUT_RATE;      Param[1] = AL_RATE_44100;
+	Param[2] = AL_CHANNEL_MODE;     Param[3] = AL_STEREO;
+	ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+	audioport = ALopenport("KDE KSCD Audio", "w", aconfig);
+
+	/* setup cdparser */
+	icdp = CDcreateparser();
+	CDaddcallback(icdp, cd_audio, (CDCALLBACKFUNC)cbplayaudio, 0);
+	CDaddcallback(icdp, cd_pnum, (CDCALLBACKFUNC)cbprognum, 0);
+	CDaddcallback(icdp, cd_atime, (CDCALLBACKFUNC)cbabstime, 0);
+
+	/* Lets handle those floating point exceptions expeditiously. */
+	sigfpe_[_UNDERFL].repls = _ZERO;
+	handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL);
+#endif
+	return 0;
+}
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+	CDSTATUS s;
+	if( CDgetstatus(d->daux, &s)==0 )
+		return -1;
+	*tracks = s.last;
+	return 0;
+}
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+	CDTRACKINFO i;
+	int ret = CDgettrackinfo(d->daux, track, &i);
+	if( ret == 0 )
+		return -1;
+	*data = 0;
+	*startframe = CDmsftoframe(i.start_min,i.start_sec,i.start_frame);
+	return 0;
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen( struct wm_drive *d, int *frames )
+{
+	CDSTATUS s;
+	if( CDgetstatus(d->daux, &s)==0 )
+		return -1;
+	*frames = CDmsftoframe(s.total_min,s.total_sec,s.total_frame);
+	return 0;
+}
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, enum wm_cd_modes oldmode,
+                      enum wm_cd_modes *mode, int *pos, int *track,
+                      int *index )
+{
+#ifdef CDDA
+	*mode = playing;
+	*track = cdtrack;
+	*pos = cdframe;
+	*index = 0;
+#else
+	CDSTATUS s;
+	if( CDgetstatus(d->daux, &s)==0 )
+		return -1;
+	*pos = CDmsftoframe(s.min,s.sec,s.frame);
+	*track = s.track;
+	*index = 0;
+	switch( s.state )
+	{
+		case CD_READY:	*mode = WM_CDM_STOPPED;
+						break;
+		case CD_STILL:
+		case CD_PAUSED: *mode = WM_CDM_PAUSED;
+						break;
+		case CD_PLAYING: *mode = WM_CDM_PLAYING;
+						break;
+		default:		*mode = WM_CDM_UNKNOWN;
+	}
+#endif
+	return 0;
+}
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+	long Param[4];
+	Param[0] = AL_LEFT_SPEAKER_GAIN;      Param[1] = left*255/100;
+	Param[2] = AL_RIGHT_SPEAKER_GAIN;     Param[3] = right*255/100;
+	ALsetparams(AL_DEFAULT_DEVICE, Param, 4);
+	return 0;
+}
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+	long Param[4];
+	Param[0] = AL_LEFT_SPEAKER_GAIN;      Param[1] = 0;
+	Param[2] = AL_RIGHT_SPEAKER_GAIN;     Param[3] = 0;
+	ALgetparams(AL_DEFAULT_DEVICE, Param, 4);
+	*left = Param[1] * 100 / 255;
+	*right = Param[3] * 100 / 255;
+	return 0;
+}
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause( struct wm_drive *d )
+{
+#ifdef CDDA
+	playing = WM_CDM_PAUSED;
+#else
+    CDSTATUS s;
+    if( CDgetstatus(d->daux, &s)==0 )
+        return -1;
+    if(s.state == CD_PLAYING)
+	    CDtogglepause(d->daux);
+#endif
+	return 0;
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume( struct wm_drive *d )
+{
+#ifdef CDDA
+	playing = WM_CDM_PLAYING;
+	signal(SIGALRM, alarmsignal);
+	setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+    CDSTATUS s;
+    if( CDgetstatus(d->daux, &s)==0 )
+        return -1;
+    if(s.state == CD_PAUSED)
+	    CDtogglepause(d->daux);
+#endif
+	return 0;
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop( struct wm_drive *d )
+{
+#ifdef CDDA
+	playing = WM_CDM_STOPPED;
+#else
+	CDstop(d->daux);
+#endif
+	return 0;
+}
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play( struct wm_drive *d, int start, int end )
+{
+#ifdef CDDA
+	int m, s, f;
+	CDframetomsf(start, &m, &s, &f);
+	CDseek(icd, m, s, f);
+	cdstopframe = end;
+	playing = PLAYING;
+	signal(SIGALRM, alarmsignal);
+	setitimer(ITIMER_REAL, &audiotimer, NULL);
+#else
+	int m, s, f;
+	CDframetomsf(start, &m, &s, &f);
+	CDplayabs(d->daux, m, s, f, 1);
+#endif
+	return 0;
+}
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject( struct wm_drive *d )
+{
+#ifdef CDDA
+	playing = WM_CDM_STOPPED;
+#endif
+	CDeject(d->daux);
+	return 0;
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*-------------------------------------------------------------*
+ * Open the CD and figure out which kind of drive is attached.
+ *-------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+	int	fd;
+	CDSTATUS s;
+
+	if (d->fd < 0)		/* Device already open? */
+	{
+		if (cd_device == NULL)
+			cd_device = DEFAULT_CD_DEVICE;
+
+		d->fd = 1;
+
+		/* Now fill in the relevant parts of the wm_drive structure. */
+		fd = d->fd;
+		*d = *(find_drive_struct("", "", ""));
+		d->fd = fd;
+		(d->init)(d);
+
+		d->daux = CDopen(cd_device,"r");
+		if (d->daux == 0)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+#ifdef CDDA
+		icd = d->daux;
+#endif
+	}
+
+	CDgetstatus(d->daux, &s);
+	if( s.state==CD_NODISC || s.state==CD_ERROR )
+		return 1;
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+/*----------------------------------*
+ * Send a SCSI command out the bus.
+ *----------------------------------*/
+int 
+wm_scsi(d, xcdb, cdblen, retbuf, retbuflen, getreply)
+	struct wm_drive *d;
+	unsigned char *xcdb;
+	int cdblen;
+	int getreply;
+	char *retbuf;
+	int retbuflen;
+{
+	return -1;
+}
+
+#endif
+
diff --git a/ascd/libworkman/plat_linux.c b/ascd/libworkman/plat_linux.c
new file mode 100644
index 0000000..4b674a8
--- /dev/null
+++ b/ascd/libworkman/plat_linux.c
@@ -0,0 +1,866 @@
+/*
+ * $Id: plat_linux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Linux-specific drive control routines.  Very similar to the Sun module.
+ */
+
+#ifdef linux
+/* Id for ident command */
+static char plat_linux_id[] = "$Id: plat_linux.c,v 1.8 1999/06/17 06:48:03 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "include/wm_config.h"
+
+#if defined(BSD_MOUNTTEST)
+  #include <mntent.h>
+#else
+  /*
+   * this is for glibc 2.x which defines ust structure in
+   * ustat.h not stat.h
+   */
+  #ifdef __GLIBC__
+    #include <sys/ustat.h>
+  #endif
+#endif
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+#include "include/wm_cdda.h"
+#include "include/wm_struct.h"
+#include "include/wm_platform.h"
+#include "include/wm_cdrom.h"
+#include "include/wm_scsi.h"
+#include "include/wm_helpers.h"
+
+#ifdef OSS_SUPPORT
+#include <linux/soundcard.h>
+#define CD_CHANNEL SOUND_MIXER_CD
+#endif
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+ 
+#ifdef LINUX_SCSI_PASSTHROUGH
+/* this is from <scsi/scsi_ioctl.h> */
+# define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+int	min_volume = 0;
+int	max_volume = 255;
+
+#ifdef OSS_SUPPORT
+int mixer;
+char mixer_dev_name[20] = "/dev/mixer";
+#endif
+extern char	*cd_device, *cddaslave_path;
+
+int	cdda_slave = -1;
+
+/*
+ * Wait for an acknowledgement from the CDDA slave.
+ */
+static int
+get_ack(int fd)
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	struct cdda_block	blk;
+
+	do
+		if (read(fd, &blk, sizeof(blk)) <= 0)
+			return (0);
+	while (blk.status != WMCDDA_ACK);
+#endif /* } */
+
+	return (1);
+} /* get_ack() */
+
+/*--------------------------------------------------------*
+ * Initialize the drive.  A no-op for the generic driver.
+ *--------------------------------------------------------*/
+int
+gen_init( struct wm_drive *d )
+{
+#ifdef OSS_SUPPORT
+/*
+ * Open and use the mixer device to set volume
+ */
+	if( ( mixer = open( mixer_dev_name, O_RDWR, 0 ) ) == -1 )
+	  {
+	  	perror( mixer_dev_name );
+	  	exit( -1 );
+	  }
+#endif
+	return (0);
+} /* gen_init() */
+
+/*
+ * Try to initialize the CDDA slave.  Returns 0 on error.
+ */
+int
+cdda_init( struct wm_drive *d )
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	int	slavefds[2];
+
+	if (cdda_slave > -1)
+		return (1);
+
+	fprintf( stderr, "slave okay\n" );
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
+	{
+		perror("socketpair");
+		return (0);
+	}
+
+	fprintf( stderr, "going to fork\n" );
+	switch (fork()) {
+	case 0:
+		close(slavefds[0]);
+		dup2(slavefds[1], 1);
+		dup2(slavefds[1], 0);
+		close(slavefds[1]);
+		close(d->fd);
+		/* Try the default path first. */
+		execl(cddaslave_path, cddaslave_path, cd_device, NULL);
+		/* Search $PATH if that didn't work. */
+		execlp("cddaslave", "cddaslave", cd_device, NULL);
+		perror(cddaslave_path);
+		exit(1);
+
+	case -1:
+		close(slavefds[0]);
+		close(slavefds[1]);
+		perror("fork");
+		return (0);
+	}
+
+	close(slavefds[1]);
+	cdda_slave = slavefds[0];
+
+	if (!get_ack(cdda_slave))
+	{
+		fprintf( stderr, "get_ack failed\n" );
+		cdda_slave = -1;
+/*		codec_start(); */
+		return (0);
+	}
+
+	return (1);
+
+#else /* BUILD_CDDA } { */
+	/*
+	 * If we're not building CDDA support, don't even bother trying.
+	 */
+	return (0);
+#endif
+} /* cdda_init()  */
+
+/*
+ * Turn off the CDDA slave.
+ */
+void
+cdda_kill( struct wm_drive *d )
+{
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "Q", 1);
+		get_ack(cdda_slave);
+		wait(NULL);
+		cdda_slave = -1;
+/*		codec_start(); */
+	}
+} /* cdda_kill() */
+
+/*-------------------------------------*
+ * Get the number of tracks on the CD.
+ *-------------------------------------*/
+int
+gen_get_trackcount(struct wm_drive *d, int *tracks)
+{
+	struct cdrom_tochdr	hdr;
+
+	if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+		return (-1);
+	
+	*tracks = hdr.cdth_trk1;
+	return (0);
+} 
+
+/*---------------------------------------------------------*
+ * Get the start time and mode (data or audio) of a track.
+ *---------------------------------------------------------*/
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+	struct cdrom_tocentry	entry;
+
+	entry.cdte_track = track;
+	entry.cdte_format = CDROM_MSF;
+
+	if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+		return (-1);
+	
+	*startframe =	entry.cdte_addr.msf.minute * 60 * 75 +
+			entry.cdte_addr.msf.second * 75 +
+			entry.cdte_addr.msf.frame;
+	*data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+	
+	return (0);
+}
+
+/*-------------------------------------*
+ * Get the number of frames on the CD.
+ *-------------------------------------*/
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+	int		tmp;
+
+	return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
+}
+
+
+/* Alarm signal handler. 
+static void do_nothing(int x) { x++; }
+*/
+
+/*--------------------------------------------------------------------------*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ *--------------------------------------------------------------------------*/
+int
+gen_get_drive_status( struct wm_drive *d, enum wm_cd_modes oldmode,
+                      enum wm_cd_modes *mode, int *pos, int *track, int *index )
+{
+	struct cdrom_subchnl		sc;
+
+#ifdef SBPCD_HACK
+	static int prevpos = 0;
+#endif
+	
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d))
+		{
+
+			case -1:	/* error */
+				return (-1);
+
+			case 1:		/* retry */
+				return (0);
+		}
+	}
+
+	sc.cdsc_format = CDROM_MSF;
+
+	if (ioctl(d->fd, CDROMSUBCHNL, &sc))
+		return (0);
+
+	switch (sc.cdsc_audiostatus) {
+	case CDROM_AUDIO_PLAY:
+		*mode = WM_CDM_PLAYING;
+		*track = sc.cdsc_trk;
+		*index = sc.cdsc_ind;
+		*pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+			sc.cdsc_absaddr.msf.second * 75 +
+			sc.cdsc_absaddr.msf.frame;
+#ifdef SBPCD_HACK
+		if( *pos < prevpos )
+                  {
+                    if( (prevpos - *pos) < 75 )
+                    {
+                      *mode = WM_CDM_TRACK_DONE;
+                    }
+                  }
+                  
+                prevpos = *pos;
+#endif
+		break;
+
+	case CDROM_AUDIO_PAUSED:
+	case CDROM_AUDIO_NO_STATUS:
+	case CDROM_AUDIO_INVALID: /**/
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			*track = sc.cdsc_trk;
+			*index = sc.cdsc_ind;
+			*pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+				sc.cdsc_absaddr.msf.second * 75 +
+				sc.cdsc_absaddr.msf.frame;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case CDROM_AUDIO_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	default:
+		*mode = WM_CDM_UNKNOWN;
+		break;
+	}
+
+	return (0);
+}
+
+/*------------------------------------------------------------------------*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ * This is not used if sound card support is enabled.
+ *
+ *------------------------------------------------------------------------*/
+#ifndef OSS_SUPPORT
+static int
+scale_volume( int vol, int max )
+{
+#ifdef CURVED_VOLUME
+        return ((max * max - (max - vol) * (max - vol)) *
+                        (max_volume - min_volume) / (max * max) + min_volume);                        
+#else
+        return ((vol * (max_volume - min_volume)) / max + min_volume);
+#endif        
+} /* scale_volume() */
+#endif
+
+/*---------------------------------------------------------------------*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ *---------------------------------------------------------------------*/
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+#ifndef OSS_SUPPORT
+	struct	cdrom_volctrl v;
+#endif
+	int	vol;
+
+#ifdef OSS_SUPPORT
+
+	left = left < 0 ? 0 : left > 100 ? 100 : left;
+	right = right < 0 ? : right > 100 ? 100 : right;
+
+	vol = ( 0x007f & left ) | ( 0x7f00 & ( right << 8 ) );
+	if( ioctl( mixer, MIXER_WRITE( CD_CHANNEL ), &vol ) == -1 )
+	  {
+	  	perror( "MIXER_WRITE" );
+	  	return ( -1 );
+	  }
+	return ( 0 );  
+#else
+
+/* Adjust the volume to make up for the CD-ROM drive's weirdness. */
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+	v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left;
+	v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right;
+	
+	return (ioctl(d->fd, CDROMVOLCTRL, &v));
+# endif	
+}
+
+/*---------------*
+ * Pause the CD.
+ *---------------*/
+int
+gen_pause(d)
+	struct wm_drive	*d;
+{
+	return (ioctl(d->fd, CDROMPAUSE));
+}
+
+/*-------------------------------------------------*
+ * Resume playing the CD (assuming it was paused.)
+ *-------------------------------------------------*/
+int
+gen_resume(struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDROMRESUME));
+}
+
+/*--------------*
+ * Stop the CD.
+ *--------------*/
+int
+gen_stop(struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDROMSTOP));
+}
+
+/*------------------------------------------------------------*
+ * Play the CD from one position to another (both in frames.)
+ *------------------------------------------------------------*/
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+	struct cdrom_msf		msf;
+
+	msf.cdmsf_min0 = start / (60*75);
+	msf.cdmsf_sec0 = (start % (60*75)) / 75;
+	msf.cdmsf_frame0 = start % 75;
+	msf.cdmsf_min1 = end / (60*75);
+	msf.cdmsf_sec1 = (end % (60*75)) / 75;
+	msf.cdmsf_frame1 = end % 75;
+
+#ifndef FAST_IDE
+	if (ioctl(d->fd, CDROMSTART))
+		return (-1);
+#endif		
+	if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+		return (-2);
+	
+	return (0);
+}
+
+/*----------------------------------------*
+ * Eject the current CD, if there is one.
+ *----------------------------------------*/
+int
+gen_eject(struct wm_drive *d)
+{
+	struct stat	stbuf;
+#if !defined(BSD_MOUNTTEST)
+	struct ustat	ust;
+#else
+	struct mntent *mnt;
+	FILE *fp;
+#endif
+	
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+#if !defined(BSD_MOUNTTEST)
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+#else
+	/*
+         * This is the same test as in the WorkBone interface.
+         * I should eliminate it there, because there is no need
+         * for it in the UI
+	 */ 
+	/* check if drive is mounted (from Mark Buckaway's cdplayer code) */
+	/* Changed it again (look at XPLAYCD from ????                    */
+	/* It's better to check the device name rather than one device is */
+	/* mounted as iso9660. That prevents "no playing" if you have more*/
+	/* than one CD-ROM, and one of them is mounted, but it's not the  */
+	/* audio CD                                              -dirk    */
+	if ((fp = setmntent (MOUNTED, "r")) == NULL)
+	{
+		fprintf (stderr, "Could not open %s: %s\n", MOUNTED, strerror (errno));
+		return(-3);
+	}
+	while ((mnt = getmntent (fp)) != NULL)
+	{
+		if (strcmp (mnt->mnt_fsname, cd_device) == 0)
+		{
+			fputs ("CDROM already mounted (according to mtab). Operation aborted.\n", stderr);
+			endmntent (fp);
+			return(-3);
+		}
+    	}
+	endmntent (fp);
+#endif /* BSD_MOUNTTEST */
+
+	if (ioctl(d->fd, CDROMEJECT))
+		return (-1);
+/*------------------
+ * Things in "foobar_one" are left over from 1.4b3
+ * I put them here for further observation. In 1.4b3, however,
+ * that workaround didn't help at least for /dev/sbpcd
+ * (The tray closed just after ejecting because re-opening the
+ * device causes the tray to close)
+ *------------------*/
+#ifdef foobar_one 
+extern int intermittent_dev
+        /*
+	 * Some drives (drivers?) won't recognize a new CD if we leave the
+         * device open.
+         */
+	if (intermittent_dev)
+	{
+		close(d->fd);
+		d->fd = -1;
+	}			  
+#endif
+	
+	return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+#ifdef CDROMCLOSETRAY
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n");
+	if (ioctl(d->fd, CDROMCLOSETRAY))
+		return (-1);
+#else
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n");
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#endif /* CDROMCLOSETRAY */
+#else /* CAN_CLOSE */
+	/* Always succeed if the drive can't close. */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+/*--------------------------------*
+ * Keep the CD open all the time.
+ * disabled, analogous to 1.4b3
+ *--------------------------------*
+void
+keep_cd_open( void )
+{
+	int	fd;
+	struct flock	fl;
+	extern	end;
+
+
+	for (fd = 0; fd < 256; fd++)
+		close(fd);
+
+	if (fork())
+		exit(0);
+
+	if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0)
+		exit(0);
+	fl.l_type = F_WRLCK;
+	fl.l_whence = 0;
+	fl.l_start = 0;
+	fl.l_len = 0;
+	if (fcntl(fd, F_SETLK, &fl) < 0)
+		exit(0);
+
+	if (open(cd_device, 0) >= 0)
+	{
+		brk(&end);
+		pause();
+	}
+
+	exit(0);
+}
+*/
+
+/*---------------------------------------------------------------------*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ *---------------------------------------------------------------------*/
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	struct cdda_block	blk;
+
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "G", 1);
+		get_ack(cdda_slave);
+		read(cdda_slave, &blk, sizeof(blk));
+
+		*left = *right = (blk.volume * 100 + 254) / 255;
+
+		if (blk.balance < 110)
+			*right = (((blk.volume * blk.balance + 127) / 128) *
+				  100 + 254) / 255;
+		else if (blk.balance > 146)
+			*left = (((blk.volume * (255 - blk.balance) +
+				   127) / 128) * 100 + 254) / 255;
+
+		return (0);
+	}
+#else /* } */
+#ifdef OSS_SUPPORT
+	int vol;
+
+	if( ioctl( mixer, MIXER_READ( CD_CHANNEL ), &vol ) == -1 )
+	  {
+	  	perror( "MIXER_READ" );
+	  	*left = *right = -1;
+	  }
+	*right = 0x007f & ( vol >> 8 );
+	*left = 0x007f & vol;  
+
+#else
+	/* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+	*left = *right = -1;
+
+#endif
+#endif
+	return (0);
+}
+
+#ifdef BUILD_CDDA /* { */
+
+/*
+ * Tell the CDDA slave to set the play direction.
+ */
+void
+gen_set_direction( int newdir )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 'd';
+		buf[1] = newdir;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to set the play speed.
+ */
+void
+gen_set_speed( int speed )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 's';
+		buf[1] = speed;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to set the loudness level.
+ */
+void
+gen_set_loudness( int loud )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 'L';
+		buf[1] = loud;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to start (or stop) saving to a file.
+ */
+void
+gen_save( char *filename )
+{
+	int	len;
+
+	if (filename == NULL || filename[0] == '\0')
+		len = 0;
+	else
+		len = strlen(filename);
+	write(cdda_slave, "F", 1);
+	write(cdda_slave, &len, sizeof(len));
+	if (len)
+		write(cdda_slave, filename, len);
+	get_ack(cdda_slave);
+}
+
+#endif /* BUILD_CDDA } */
+
+/*---------------------------------------------*
+ * Send an arbitrary SCSI command to a device.
+ *---------------------------------------------*/
+int
+wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
+	 void *retbuf, int retbuflen, int getreply )
+{
+#ifdef LINUX_SCSI_PASSTHROUGH
+
+        char *cmd;
+	int cmdsize;
+
+	cmdsize = 2 * sizeof(int);
+	if (retbuf)
+	  {
+	    if (getreply) cmdsize += max(cdblen, retbuflen);
+	    else cmdsize += (cdblen + retbuflen);
+	  }
+	else cmdsize += cdblen;
+	  
+	cmd = malloc(cmdsize);
+	if (cmd == NULL)
+	  return (-1);
+
+	((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0);
+	((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0);
+
+	memcpy(cmd + 2*sizeof(int), cdb, cdblen);
+	if (retbuf && !getreply)
+	  memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen);
+	
+	if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd))
+	  {
+	    wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__);
+	    wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n");
+	    wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n",
+	            cmd[0],  cmd[1],  cmd[2],  cmd[3],  cmd[4],  cmd[5]);
+	    free(cmd);
+	    return (-1);
+	  }
+	
+	if (retbuf && getreply)
+	  memcpy(retbuf, cmd + 2*sizeof(int), retbuflen);
+
+	free(cmd);
+	return 0;
+
+#else /* Linux SCSI passthrough*/
+	return (-1);
+#endif
+}
+
+/*---------------------------------------------------------------------------*
+ * Open the CD device and figure out what kind of drive is attached.
+ *---------------------------------------------------------------------------*/
+int
+wmcd_open( struct wm_drive *d )
+{
+	int		fd;
+	static int	warned = 0;
+	int		retval = 0;
+	char vendor[32], model[32], rev[32];
+ 
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	if (d->fd >= 0)		/* Device already open? */
+	{
+		wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);
+		return (0);
+	}
+	
+	d->fd = open(cd_device, O_RDONLY | O_NONBLOCK);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+/* Hack proposed by Carey Evans, introduced by Debian maintainer :
+ * treat EIO like ENXIO since some Linux drives do never return ENXIO
+ */
+		else if ((errno != ENXIO) && (errno != EIO) && (errno != ENOMEDIUM))
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		retval = 1;
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+#ifdef LINUX_SCSI_PASSTHROUGH
+	/* Can we figure out the drive type? */
+	wm_scsi_get_drive_type(d, vendor, model, rev);
+#endif
+	*d = *(find_drive_struct(vendor, model, rev));	
+	wm_drive_settype(vendor, model, rev);
+
+	d->fd = fd;
+	(d->init)(d);
+	return retval;
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif /* linux */
diff --git a/ascd/libworkman/plat_linux_audio.c b/ascd/libworkman/plat_linux_audio.c
new file mode 100644
index 0000000..c761e9f
--- /dev/null
+++ b/ascd/libworkman/plat_linux_audio.c
@@ -0,0 +1,490 @@
+/*
+ * $Id: plat_linux_audio.c,v 1.3 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Linux digital audio functions.
+ */
+
+#include "include/wm_config.h"
+
+#if defined(linux) && defined(BUILD_CDDA) /* { */
+
+static char plat_linux_audio_id[] = "$Id: plat_linux_audio.c,v 1.3 1999/03/07 08:36:40 dirk Exp $";
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wm_cdda.h */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker.  Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information.  When we write some
+ * sound to the audio device, we put its status info into the queue.  We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure.  When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block	queue[QSIZE];
+int			qtail;
+int			qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int	aufd, aucfd;
+static int	raw_audio = 1;	/* Can /dev/audio take 44.1KHz stereo? */
+
+/* 
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+	signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+	audio_info_t		info;
+	char			*audiodev, *acdev;
+	int			linval;
+
+	audiodev = getenv("AUDIODEV");
+	if (audiodev == NULL)
+		audiodev = "/dev/audio";
+
+	acdev = malloc(strlen(audiodev) + 4);
+	if (acdev == NULL)
+	{
+		perror("Can't allocate audio control filename");
+		exit(1);
+	}
+	strcpy(acdev, audiodev);
+	strcat(acdev, "ctl");
+
+	aucfd = open(acdev, O_WRONLY, 0);
+	if (aucfd < 0)
+	{
+		perror(acdev);
+		exit(1);
+	}
+	free(acdev);
+
+	aufd = open(audiodev, O_WRONLY, 0);
+	if (aufd < 0)
+	{
+		perror(audiodev);
+		exit(1);
+	}
+
+	signal(SIGALRM, dummy);
+
+	/*
+	 * Try to set the device to CD-style audio; we can process it
+	 * with the least CPU overhead.
+	 */
+	AUDIO_INITINFO(&info);
+	info.play.sample_rate = 44100;
+	info.play.channels = 2;
+	info.play.precision = 16;
+	info.play.encoding = AUDIO_ENCODING_LINEAR;
+	info.play.pause = 0;
+	info.record.pause = 0;
+	info.monitor_gain = 0;
+
+	if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+		if (errno == EINVAL)
+		{
+			/*
+			 * Oh well, so much for that idea.
+			 */
+			AUDIO_INITINFO(&info);
+			info.play.sample_rate = 8000;
+			info.play.channels = 1;
+			info.play.precision = 8;
+			info.play.encoding = AUDIO_ENCODING_ULAW;
+			info.play.pause = 0;
+			info.record.pause = 0;
+			info.monitor_gain = 0;
+			if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+			{
+				perror("Can't set up audio device");
+				exit(1);
+			}
+
+			/*
+			 * Initialize the linear-to-ulaw mapping table.
+			 */
+			if (ulawmap == NULL)
+				ulawmap = malloc(65536);
+			if (ulawmap == NULL)
+			{
+				perror("malloc");
+				exit(1);
+			}
+			for (linval = 0; linval < 65536; linval++)
+				ulawmap[linval] = linear_to_ulaw(linval-32768);
+			ulawmap += 32768;
+			raw_audio = 0;
+		}
+		else
+		{
+			perror(audiodev);
+			exit(1);
+		}
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+	audio_info_t		info;
+
+	/*
+	 * Start at the correct queue position.
+	 */
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	qtail = info.play.eof % QSIZE;
+	qstart = qtail;
+
+	queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+	if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+		perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+	wmaudio_stop();
+	close(aufd);
+	close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+	audio_info_t		info;
+
+	AUDIO_INITINFO(&info);
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	info.play.gain = level;
+	if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+	audio_info_t		info;
+
+	AUDIO_INITINFO(&info);
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	level *= AUDIO_RIGHT_BALANCE;
+	info.play.balance = level / 255;
+	if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+	queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+	audio_info_t		info;
+	int			qhead;
+
+	/*
+	 * Now send the most current status information to our parent.
+	 */
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+		perror("AUDIO_GETINFO");
+	qhead = info.play.eof % QSIZE;
+
+	if (qhead != qstart && playing)
+	{
+		int	balance;
+
+		if (queue[qhead].status != WMCDDA_DONE)
+			queue[qhead].status = WMCDDA_PLAYED;
+		queue[qhead].volume = info.play.gain;
+		queue[qhead].balance = (info.play.balance * 255) /
+					AUDIO_RIGHT_BALANCE;
+
+		send_status(queue + qhead);
+		qstart = -1;
+	}
+
+	return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+	int			i;
+	short			*buf16;
+	int			alarmcount = 0;
+	struct itimerval	it;
+
+	alarm(1);
+
+	while (write(aufd, rawbuf, buflen) <= 0)
+		if (errno == EINTR)
+		{
+			if (! raw_audio && alarmcount++ < 5)
+			{
+				/*
+				 * 8KHz /dev/audio blocks for several seconds
+				 * waiting for its queue to drop below a low
+				 * water mark.
+				 */
+				wmaudio_send_status();
+				timerclear(&it.it_interval);
+				timerclear(&it.it_value);
+				it.it_value.tv_usec = 500000;
+				setitimer(ITIMER_REAL, &it, NULL);
+				continue;
+			}
+
+/*			close(aufd);
+			close(aucfd);
+			wmaudio_init();
+*/ wmaudio_stop( void );
+			alarm(2);
+			continue;
+		}
+		else
+		{
+			blk->status = WMCDDA_ERROR;
+			return (-1);
+		}
+	alarm(0);
+
+	/*
+	 * Mark this spot in the audio stream.
+	 *
+	 * Marks don't always succeed (if the audio buffer is empty
+	 * this call will block forever) so do it asynchronously.
+	 */
+	fcntl(aufd, F_SETFL, O_NONBLOCK);
+	if (write(aufd, rawbuf, 0) < 0)
+	{
+		if (errno != EAGAIN)
+			perror("audio mark");
+	}
+	else
+		qtail = (qtail + 1) % QSIZE;
+
+	fcntl(aufd, F_SETFL, 0);
+
+	queue[qtail] = *blk;
+
+	if (wmaudio_send_status() < 0)
+		return (-1);
+	else
+		return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+	audio_info_t		info;
+	int			balance;
+
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+		perror("AUDIO_GETINFO");
+	blk->volume = info.play.gain;
+	blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711  (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+**     Continuous PCM Companding Law," Villeret, Michel,
+**     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+**     1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+**     for Analog-to_Digital Conversion Techniques,"
+**     17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+ 
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+	static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+	int sign, exponent, mantissa;
+	unsigned char ulawbyte;
+ 
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;            /* set aside the sign */
+	if ( sign != 0 ) sample = -sample;              /* get magnitude */
+	if ( sample > CLIP ) sample = CLIP;             /* clip the magnitude */
+ 
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[( sample >> 7 ) & 0xFF];
+	mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+	ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+	if ( ulawbyte == 0 ) ulawbyte = 0x02;   /* optional CCITT trap */
+#endif
+ 
+	return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+	short		*buf16 = (short *)rawbuf;
+	int		i, j, samples;
+	int		mono_value;
+	unsigned char	*rbend = rawbuf + buflen;
+
+	/* Don't do anything if the audio device can take the raw values. */
+	if (raw_audio)
+		return (buflen);
+
+	for (i = 0; buf16 < (short *)(rbend); i++)
+	{
+		/* Downsampling to 8KHz is a little irregular. */
+		samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+		/* And unfortunately, we don't always end on a nice boundary. */
+		if (buf16 + samples > (short *)(rbend))
+			samples = ((short *)rbend) - buf16;
+
+		/*
+		 * No need to average all the values; taking the first one
+		 * is sufficient and less CPU-intensive.  But we do need to
+		 * do both channels.
+		 */
+		mono_value = (buf16[0] + buf16[1]) / 2;
+		buf16 += samples;
+		rawbuf[i] = ulawmap[mono_value];
+	}
+
+	return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/ascd/libworkman/plat_linux_cdda.c b/ascd/libworkman/plat_linux_cdda.c
new file mode 100644
index 0000000..babd4dd
--- /dev/null
+++ b/ascd/libworkman/plat_linux_cdda.c
@@ -0,0 +1,371 @@
+/*
+ * $Id: plat_linux_cdda.c,v 1.3 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Linux CDDA functions. Derived from the SUN module.
+ */
+
+#include "include/wm_config.h"
+ 
+#if defined(linux) && defined(BUILD_CDDA) /* { */
+
+static char plat_linux_cdda_id[] = "$Id: plat_linux_cdda.c,v 1.3 1999/03/07 08:36:40 dirk Exp $";
+
+
+#include "include/wm_cdda.h"
+/* types.h and cdio.h are included by wmcdda.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <errno.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2368
+#define SAMPLES_PER_BLK 588
+
+/* Address of next block to read. */
+int	current_position = 0;
+
+/* Address of first and last blocks to read. */
+int	starting_position = 0;
+int	ending_position = 0;
+
+/* Playback direction. */
+int	direction = 1;
+
+/* Number of blocks to read at once; initialize to the maximum. */
+int	numblocks = 30;
+
+/*
+ * This is the fastest way to convert from BCD to 8-bit.
+ */
+unsigned char unbcd[256] = {
+  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,0,0,0,0,0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,0,0,0,0,0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  0,0,0,0,0,0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,  0,0,0,0,0,0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,  0,0,0,0,0,0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,  0,0,0,0,0,0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,  0,0,0,0,0,0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  0,0,0,0,0,0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,  0,0,0,0,0,0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,  0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ * NOTE: We allocate twice as much space as we need to actually read a block;
+ * this lets us do audio manipulations without bothering to malloc a second
+ * buffer.
+ *
+ * Also, test to see if we can actually *do* CDDA on this drive; if not, we
+ * need to exit right away so the UI doesn't show the user any CDDA controls.
+ */
+int
+wmcdda_init(char **bufadr, long *buflenadr, int init_fd, char *devname)
+{
+	struct cdrom_cdda	cdda;
+
+	*bufadr = malloc(numblocks * CDDABLKSIZE * 2);
+	if (*bufadr == NULL)
+		return (-1);
+
+	*buflenadr = numblocks * CDDABLKSIZE;
+
+	/*init_fd = open(devname, 0);
+	if (init_fd == -1)
+		init_fd = open("/dev/rdsk/c0t6d0s2", 0);
+	*/
+	init_fd = wmcdda_open(devname);
+
+	if (init_fd > -1)
+	{
+		cdda.cdda_addr = 200;
+		cdda.cdda_length = 1;
+		cdda.cdda_data = *bufadr;
+		cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+		if (ioctl(init_fd, CDROMCDDA, &cdda) < 0)
+		{
+			close(init_fd);
+			init_fd = -1;
+		}
+	}
+
+	return (init_fd);
+}
+
+/*
+ * Try to open the CD device
+ */
+int
+wmcdda_open(char *devname)
+{
+    int fd;
+
+    fd = open(devname, 0); 
+    if (fd == -1)
+	fd = open("/dev/rdsk/c0t6d0s2", 0);
+
+    return(fd);
+}
+
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+void
+wmcdda_close(int fd)
+{
+	close(fd);
+}
+
+/*
+ * Set up for playing the CD.  Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+void
+wmcdda_setup(int start, int end, int realstart)
+{
+	current_position = start - 150;
+	ending_position = end - 150;
+	starting_position = realstart - 150;
+
+	/*
+	 * Special case: don't start at the "end" of a track if we're
+	 * playing backwards!
+	 */
+	if (direction == -1 && start == realstart)
+		current_position = ending_position - numblocks;
+}
+
+/*
+ * Read some blocks from the CD.  Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(int fd, unsigned char *rawbuf, long buflen,
+	struct cdda_block *block)
+{
+	struct cdrom_cdda	cdda;
+	int			blk;
+	unsigned char		*q;
+	extern int		speed;
+
+	if ((direction > 0 && current_position >= ending_position) ||
+	    (direction < 0 && current_position < starting_position))
+	{
+		block->status = WMCDDA_DONE;
+		return (0);
+	}
+
+	cdda.cdda_addr = current_position;
+	if (ending_position && current_position + numblocks > ending_position)
+		cdda.cdda_length = ending_position - current_position;
+	else
+		cdda.cdda_length = numblocks;
+	cdda.cdda_data = rawbuf;
+	cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+	if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+	{
+		if (errno == ENXIO)	/* CD ejected! */
+		{
+			block->status = WMCDDA_EJECTED;
+			return (-1);
+		}
+
+		if (current_position + numblocks > ending_position)
+		{
+			/*
+			 * Hit the end of the CD, probably.
+			 */
+			block->status = WMCDDA_DONE;
+			return (0);
+		}
+
+		/* Sometimes it fails once, dunno why */
+		if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+		{
+			if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+			{
+				if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+				{
+					perror("CDROMCDDA");
+					block->status = WMCDDA_ERROR;
+					return (-1);
+				}
+			}
+		}
+	}
+
+	if (speed > 148)
+	{
+		/*
+		 * We want speed=148 to advance by cdda_length, but
+		 * speed=256 to advance cdda_length * 4.
+		 */
+		current_position = current_position +
+			(cdda.cdda_length * direction * (speed - 112)) / 36;
+	}
+	else
+		current_position = current_position +
+			cdda.cdda_length * direction;
+
+	for (blk = 0; blk < numblocks; blk++)
+	{
+		/*
+		 * New valid Q-subchannel information?  Update the block
+		 * status.
+		 */
+		q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
+		if (*q == 1)
+		{
+			block->status = WMCDDA_OK;
+			block->track =  unbcd[q[1]];
+			block->index =  unbcd[q[2]];
+			block->minute = unbcd[q[7]];
+			block->second = unbcd[q[8]];
+			block->frame =  unbcd[q[9]];
+		}
+	}
+
+	return (cdda.cdda_length * CDDABLKSIZE);
+}
+
+/*
+ * Normalize a bunch of CDDA data.  Basically this means ripping out the
+ * Q subchannel data and doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ *
+ * Scanning is handled here too.
+ *
+ * XXX - do byte swapping on Intel boxes?
+ */
+long
+wmcdda_normalize(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+	int		i, nextq;
+	int		blocks = buflen / CDDABLKSIZE;
+	unsigned char	*dest = rawbuf;
+	unsigned char	tmp;
+	long		*buf32 = (long *)rawbuf, tmp32;
+
+/*
+ * this was #ifndef LITTLEENDIAN
+ * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
+ */
+#if WM_BIG_ENDIAN
+	if (blocks--)
+		for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+		{
+			/* Only need to use temp buffer on first block. */
+			tmp = *rawbuf++;
+			*dest++ = *rawbuf++;
+			*dest++ = tmp;
+		}
+#endif
+
+	while (blocks--)
+	{
+		/* Skip over Q data. */
+		rawbuf += 16;
+
+		for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+		{
+#if WM_LITTLE_ENDIAN
+			*dest++ = *rawbuf++;
+			*dest++ = *rawbuf++;
+#else
+			*dest++ = rawbuf[1];
+			*dest++ = rawbuf[0];
+			rawbuf += 2;
+#endif
+		}
+	}
+
+	buflen -= ((buflen / CDDABLKSIZE) * 16);
+
+	/*
+	 * Reverse the data here if we're playing backwards.
+	 * XXX - ideally this should be done above.
+	 */
+	if (direction < 0)
+	{
+		buflen /= 4;	/* we can move 32 bits at a time. */
+
+		for (i = 0; i < buflen / 2; i++)
+		{
+			tmp32 = buf32[i];
+			buf32[i] = buf32[buflen - i - 1];
+			buf32[buflen - i - 1] = tmp32;
+		}
+
+		buflen *= 4;
+	}
+
+	return (buflen);
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+	if (newdir == 0)
+	{
+		numblocks = 20;
+		direction = 1;
+	}
+	else
+	{
+		numblocks = 30;
+		direction = -1;
+	}
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+	if (speed > 128)
+		numblocks = 12;
+	else
+		numblocks = direction > 0 ? 20 : 30;
+}
+
+#endif /* } */
diff --git a/ascd/libworkman/plat_news.c b/ascd/libworkman/plat_news.c
new file mode 100644
index 0000000..6736cd7
--- /dev/null
+++ b/ascd/libworkman/plat_news.c
@@ -0,0 +1,474 @@
+/*
+ * $Id: plat_news.c,v 1.7 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Sony NEWS-specific drive control routines.
+ */
+
+#if defined( __sony_news) || defined(sony_news)
+
+static char plat_news_id[] = "$Id: plat_news.c,v 1.7 1999/03/07 08:36:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ustat.h>
+#include <CD.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+char *strchr();
+
+extern char	*cd_device;
+extern int	intermittent_dev;
+
+int	min_volume = 128;
+int	max_volume = 255;
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init(d)
+	struct wm_drive	*d;
+{
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(d, tracks)
+	struct wm_drive	*d;
+	int		*tracks;
+{
+	struct CD_Capacity	cc;
+
+	if (CD_GetCapacity(d->fd, &cc))
+		return (-1);
+
+	*tracks = cc.etrack - 1;
+	return (0);
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(d, track, data, startframe)
+	struct wm_drive	*d;
+	int		track, *data, *startframe;
+{
+	struct CD_TOCinfo	hdr;
+	struct CD_TOCdata	ent;
+
+	hdr.strack = track;
+	hdr.ntrack = 1;
+	hdr.data = &ent;
+	if (CD_ReadTOC(d->fd, &hdr))
+		return (-1);
+
+	*data = (ent.control & 4) ? 1 : 0;
+	*startframe = ent.baddr;
+
+	return (0);
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(d, frames)
+	struct wm_drive	*d;
+	int		*frames;
+{
+	int		tmp;
+
+	if ((d->get_trackcount)(d, &tmp))
+		return (-1);
+
+	return (gen_get_trackinfo(d, tmp + 1, &tmp, frames));
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(d, oldmode, mode, pos, track, index)
+	struct wm_drive	*d;
+	enum wm_cd_modes oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	struct CD_Status		sc;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	/* Disc is ejected.  Close the device. */
+	if (CD_GetStatus(d->fd, &sc))
+	{
+		WMclose(d->fd);
+		d->fd = -1;
+		return (0);
+	}
+
+	switch (sc.status) {
+	case CDSTAT_PLAY:
+		*mode = PLAYING;
+		*track = sc.tno;
+		*index = sc.index;
+		*pos = sc.baddr;
+		break;
+
+	case CDSTAT_PAUSE:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			*track = sc.tno;
+			*index = sc.index;
+			*pos = sc.baddr;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case CDSTAT_STOP:
+		if (oldmode == WM_CDM_PLAYING)
+		{
+			*mode = WM_CDM_TRACK_DONE;	/* waiting for next track. */
+			break;
+		}
+		/* fall through */
+
+	default:
+		*mode = WM_CDM_STOPPED;
+		break;
+	}
+
+	return (0);
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(d, left, right)
+	struct wm_drive	*d;
+	int		left, right;
+{
+	/* NEWS can't adjust volume! */
+	return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(d)
+	struct wm_drive	*d;
+{
+	CD_Pause(d->fd);
+
+	return (0);
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(d)
+	struct wm_drive	*d;
+{
+	CD_Restart(d->fd);
+
+	return (0);
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(d)
+	struct wm_drive *d;
+{
+	CD_Stop(d->fd);
+
+	return (0);
+}
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(d, start, end)
+	struct wm_drive	*d;
+	int		start, end;
+{
+	struct CD_PlayAddr		msf;
+
+	msf.addrmode			= CD_MSF;
+	msf.addr.msf.startmsf.min	= start / (60*75);
+	msf.addr.msf.startmsf.sec	= (start % (60*75)) / 75;
+	msf.addr.msf.startmsf.frame	= start % 75;
+	msf.addr.msf.endmsf.min		= end / (60*75);
+	msf.addr.msf.endmsf.sec		= (end % (60*75)) / 75;
+	msf.addr.msf.endmsf.frame	= end % 75;
+
+	if (CD_Play(d->fd, &msf))
+	{
+		printf("wm_cd_play_chunk(%d,%d)\n",start,end);
+		printf("msf = %d:%d:%d %d:%d:%d\n",
+			msf.addr.msf.startmsf.min,
+			msf.addr.msf.startmsf.sec,
+			msf.addr.msf.startmsf.frame,
+			msf.addr.msf.endmsf.min,
+			msf.addr.msf.endmsf.sec,
+			msf.addr.msf.endmsf.frame);
+		perror("CD_Play");
+		return (-1);
+	}
+
+	return (0);
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(d)
+	struct wm_drive	*d;
+{
+	struct stat	stbuf;
+	struct ustat	ust;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+
+	if (CD_AutoEject(d->fd))
+		return (-1);
+
+	/* Close the device if it needs to vanish. */
+	if (intermittent_dev)
+	{
+		WMclose(d->fd);
+		d->fd = -1;
+	}
+
+	return (0);
+}
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * Close the CD device.
+ */
+int
+WMclose(fd)
+	int	fd;
+{
+	int	ret;
+
+	ret = CD_Close(fd);
+	wm_susleep(3000000);
+	return (ret);
+}
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(d, left, right)
+	struct wm_drive	*d;
+	int		*left, *right;
+{
+	/* Suns, HPs, Linux, NEWS can't read the volume; oh well */
+	*left = *right = -1;
+
+	return (0);
+}
+
+/*
+ * Pass SCSI commands to the device.
+ */
+int
+wm_scsi(d, cdb, cdblen, buf, buflen, getreply)
+	struct wm_drive	*d;
+	unsigned char	*cdb, *buf;
+	int		cdblen, buflen, getreply;
+{
+	/* NEWS can't do SCSI passthrough... or can it? */
+	return (-1);
+}
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(d)
+	struct wm_drive	*d;
+{
+	int		fd;
+	static int	warned = 0;
+	char	vendor[32] = WM_STR_GENVENDOR;
+	char	 model[32] = WM_STR_GENMODEL;
+	char	   rev[32] = WM_STR_GENREV;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+
+	intermittent_dev = 1;
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	if ((d->fd = CD_Open(cd_device, 0)) < 0)
+	{
+		/* Solaris 2.2 volume manager moves links around */
+		if (errno == ENOENT && intermittent_dev)
+			return (0);
+
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				char	realname[MAXPATHLEN];
+
+				if (realpath(cd_device, realname) == NULL)
+				{
+					perror("realpath");
+					return (0);
+				}
+
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", realname,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+		else if (errno != EIO)	/* defined at top */
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+	/* Figure out the drive type, if possible */
+	wm_scsi_get_drive_type(d, vendor, model, rev);
+	*d = *(find_drive_struct(vendor, model, rev));
+	wm_drive_settype(vendor, model, rev);
+	
+	d->fd = fd;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif
diff --git a/ascd/libworkman/plat_openbsd.c b/ascd/libworkman/plat_openbsd.c
new file mode 100644
index 0000000..bdc2fb7
--- /dev/null
+++ b/ascd/libworkman/plat_openbsd.c
@@ -0,0 +1,530 @@
+/*
+ * $Id: plat_openbsd.c,v 1.7 1999/03/07 08:36:40 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *        
+ * OpenBSD-specific drive control routines. (Based on plat_freebsd.c)
+ *
+ * Michael Shalayeff, 7/24/96
+ * Todd Pfaff, 3/20/94
+ *
+ */
+
+#if defined(__OpenBSD__)
+
+static char plat_openbsd[] = "$Id: plat_openbsd.c,v 1.7 1999/03/07 08:36:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include "include/wm_config.h"
+
+/* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#include <sys/time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/cdio.h>
+#include <sys/scsiio.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_cd.h>
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+void *malloc();
+
+int	min_volume = 10;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init(struct wm_drive *d)
+{
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(struct wm_drive *d, int tracks)
+{
+	struct ioc_toc_header	hdr;
+
+	if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1)
+		return (-1);
+
+	*tracks = hdr.ending_track - hdr.starting_track + 1;
+
+	return (0);
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
+{
+	struct ioc_read_toc_entry	toc;
+	struct cd_toc_entry		toc_buffer;
+
+	bzero((char *)&toc_buffer, sizeof(toc_buffer));
+	toc.address_format = CD_MSF_FORMAT;
+	toc.starting_track = track;
+	toc.data_len = sizeof(toc_buffer);
+	toc.data = &toc_buffer;
+
+	if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc))
+		return (-1);
+
+	*data = ((toc_buffer.control & 0x4) != 0);
+
+	*startframe = toc_buffer.addr.msf.minute*60*75 +
+		toc_buffer.addr.msf.second * 75 +
+		toc_buffer.addr.msf.frame;
+
+	return (0);
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames)
+{
+	int		tmp;
+	struct ioc_toc_header		hdr;
+	int status;
+
+#define LEADOUT 0xaa			/* see scsi.c.  what a hack! */
+	return gen_get_trackinfo(d, LEADOUT, &tmp, frames);
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(struct wm_drive *d, enum wm_cd_modes oldmode,
+		     enum wm_cd_modes *mode, int *pos, int *track, int *index)
+{
+	struct ioc_read_subchannel	sc;
+	struct cd_sub_channel_info	scd;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	sc.address_format	= CD_MSF_FORMAT;
+	sc.data_format		= CD_CURRENT_POSITION;
+	sc.track		= 0;
+	sc.data_len		= sizeof(scd);
+	sc.data			= (struct cd_sub_channel_info *)&scd;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc)) {
+	    /* we need to release the device so the kernel will notice
+	       reloaded media */
+	    (void) close(d->fd);
+	    d->fd = -1;
+		return (0);	/* ejected */
+	}
+
+	switch (scd.header.audio_status) {
+	case CD_AS_PLAY_IN_PROGRESS:
+		*mode = WM_CDM_PLAYING;
+dopos:
+		*pos = scd.what.position.absaddr.msf.minute * 60 * 75 +
+			scd.what.position.absaddr.msf.second * 75 +
+			scd.what.position.absaddr.msf.frame;
+		*track = scd.what.position.track_number;
+		*index = scd.what.position.index_number;
+		break;
+
+	case CD_AS_PLAY_PAUSED:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			goto dopos;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case CD_AS_PLAY_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	case CD_AS_NO_STATUS:
+	case 0:
+		*mode = WM_CDM_STOPPED;
+		break;
+	}
+
+	return (0);
+}
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom.  To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting.  The actual formula looks like
+ *
+ *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ *                           max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ *	return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+static int
+scale_volume(int vol, int max)
+{
+    return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(struct wm_drive *d, int left, int right)
+{
+	struct ioc_vol vol;
+
+	if (left < 0)	/* don't laugh, I saw this happen once! */
+	    left = 0;
+	if (right < 0)
+	    right = 0;
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+	bzero((char *)&vol, sizeof(vol));
+
+	vol.vol[LEFT_PORT] = left;
+	vol.vol[RIGHT_PORT] = right;
+
+	if (ioctl(d->fd, CDIOCSETVOL, &vol))
+		return (-1);
+
+	return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDIOCPAUSE));
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDIOCRESUME));
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(struct wm_drive *d)
+{
+	return (ioctl(d->fd, CDIOCSTOP));
+}
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(struct wm_drive *d, int start, int end)
+{
+	struct ioc_play_msf	msf;
+
+	msf.start_m	= start / (60*75);
+	msf.start_s	= (start % (60*75)) / 75;
+	msf.start_f	= start % 75;
+	msf.end_m	= end / (60*75);
+	msf.end_s	= (end % (60*75)) / 75;
+	msf.end_f	= end % 75;
+
+	if (ioctl(d->fd, CDIOCSTART))
+		return (-1);
+
+	if (ioctl(d->fd, CDIOCPLAYMSF, &msf))
+		return (-2);
+
+	return (0);
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+	/* On some systems, we can check to see if the CD is mounted. */
+	struct stat	stbuf;
+	struct statfs	buf;
+	int rval;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (fstatfs(stbuf.st_rdev, &buf) == 0)
+		return (-3);
+
+	rval = ioctl(d->fd, CDIOCALLOW);
+	if (rval == 0)
+	    rval = ioctl(d->fd, CDIOCEJECT);
+	if (rval == 0)
+	    rval = ioctl(d->fd, CDIOCPREVENT);
+	if (rval == 0)
+	    rval = close(d->fd);
+	if (rval == 0)
+	    d->fd = -1;
+	return rval;
+} /* gen_eject() */
+
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(int cd_vol, int max)
+{
+	int	vol = 0, top = max, bot = 0, scaled;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol == scaled)
+			break;
+		if (cd_vol < scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(struct wm_drive *d, int *left, int *right)
+{
+	struct ioc_vol vol;
+
+	if (d->fd >= 0)
+	{
+		bzero((char *)&vol, sizeof(vol));
+
+		if (ioctl(d->fd, CDIOCGETVOL, &vol))
+			*left = *right = -1;
+		else
+		{
+			*left = unscale_volume(vol.vol[LEFT_PORT], 100);
+			*right = unscale_volume(vol.vol[RIGHT_PORT], 100);
+		}
+	}
+	else
+		*left = *right = -1;
+
+	return (0);
+}
+
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ *
+ */
+int
+wm_scsi(struct wm_drive *d, unsigned char *cdb,
+	int cdblen, void *retbuf, int retbuflen, int getreply)
+{
+	return (-1);
+}
+
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(struct wm_drive *d)
+{
+	int		fd;
+	static int	warned = 0;
+	char vendor[32] = WM_STR_GENVENDOR;
+	char  model[32] = WM_STR_GENMODEL;
+	char    rev[32] = WM_STR_GENREV;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	d->fd = open(cd_device, 0);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+	*d = *(find_drive_struct(vendor, model, rev));
+	wm_drive_settype(vendor, model, rev);
+	
+	(d->init)(d);
+
+	d->fd = fd;
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif
diff --git a/ascd/libworkman/plat_osf1.c b/ascd/libworkman/plat_osf1.c
new file mode 100644
index 0000000..538871c
--- /dev/null
+++ b/ascd/libworkman/plat_osf1.c
@@ -0,0 +1,666 @@
+/*
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * OSF  drive control routines.
+ */
+
+#if defined(__osf__)
+
+static char plat_osf_id[] = "$Id: plat_osf1.c,v 1.6 1999/03/07 08:36:40 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+/* #include <sys/rzdisk.h>
+#include <sys/cdrom.h>  */
+#include <io/cam/rzdisk.h>
+#include <io/cam/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ *   This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ *                              - Chris Ross ([email protected])
+ */
+struct cd_toc_header_and_entries {
+	struct cd_toc_header	cdth;
+	struct cd_toc_entry		cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int	min_volume = 128;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM.
+ */
+void
+find_cdrom()
+{
+	char	*data, *fgetline();
+	FILE	*uerf;
+	int	fds[2];
+	int	pid;
+	extern char *getenv();
+
+	pipe(fds);
+
+	cd_device = getenv("CDROM");
+	if (cd_device != NULL)
+	    return;
+	if ((pid = fork()) == 0) {
+		close(fds[0]);
+		dup2(fds[1], 1);
+		execl("/etc/uerf", "uerf", "-R", "-r", "300", NULL);
+		execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", NULL);
+		_exit(1);
+	} else if (pid < 0) {
+		perror("fork");
+		exit(1);
+	}
+
+	close(fds[1]);
+	uerf = fdopen(fds[0], "r");
+
+	while (data = fgetline(uerf))
+		if (strstr(data, "RRD42")) {
+			char	*device;
+
+			cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+			strcpy(cd_device, "/dev/r");
+			device = strstr(data, "rz");
+			device[(int)(strchr(device, ' ') - device)] = '\0';
+			strcat(cd_device, device);
+			strcat(cd_device, "c");
+			break;
+		}
+
+	fclose(uerf);
+
+	if (cd_device == NULL) {
+		fprintf(stderr,
+			"No cdrom (RRD42) is installed on this system\n");
+		exit(1);
+	}
+
+	kill(pid, 15);
+	(void)wait((int *)NULL);
+}
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init(d)
+	struct wm_drive	*d;
+{
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(d, tracks)
+	struct wm_drive	*d;
+	int		*tracks;
+{
+	struct cd_toc_header	hdr;
+
+	if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+		return (-1);
+
+	*tracks = hdr.th_ending_track;
+
+	return (0);
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(d, track, data, startframe)
+	struct wm_drive	*d;
+	int		track, *data, *startframe;
+{
+	struct cd_toc				toc;
+	struct cd_toc_header			hdr;
+	struct cd_toc_header_and_entries	toc_buffer;
+
+	if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+		return (-1);
+
+	bzero((char *)&toc_buffer, sizeof(toc_buffer));
+	toc.toc_address_format = CDROM_MSF_FORMAT;
+	toc.toc_starting_track = 0;
+	toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+					hdr.th_data_len0) & 0xfff) + 2;
+	toc.toc_buffer = (caddr_t)&toc_buffer;
+
+	if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+		return (-1);
+
+	if (track == 0)
+		track = hdr.th_ending_track + 1;
+
+	*data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+	*startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+		toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+		toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+	return (0);
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(d, frames)
+	struct wm_drive	*d;
+	int		*frames;
+{
+	int		tmp;
+
+	return (gen_get_trackinfo(d, 0, &tmp, frames));
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(d, oldmode, mode, pos, track, index)
+	struct wm_drive	*d;
+	enum wm_cd_modes	oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	struct cd_sub_channel		sc;
+	struct cd_subc_channel_data	scd;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	sc.sch_address_format	= CDROM_MSF_FORMAT;
+	sc.sch_data_format	= CDROM_CURRENT_POSITION;
+	sc.sch_track_number	= 0;
+	sc.sch_alloc_length	= sizeof(scd);
+	sc.sch_buffer		= (caddr_t)&scd;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+		return (0);	/* ejected */
+
+	switch (scd.scd_header.sh_audio_status) {
+	case AS_PLAY_IN_PROGRESS:
+		*mode = WM_CDM_PLAYING;
+dopos:
+		*pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+			scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+			scd.scd_position_data.scp_absaddr.msf.f_units;
+		*track = scd.scd_position_data.scp_track_number;
+		*index = scd.scd_position_data.scp_index_number;
+		break;
+
+	case AS_PLAY_PAUSED:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			goto dopos;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case AS_PLAY_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	case AS_NO_STATUS:
+		*mode = WM_CDM_STOPPED;
+		break;
+	default:
+	    abort();
+	}
+
+	return (0);
+}
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom.  To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting.  The actual formula looks like
+ *
+ *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ *                           max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ *	return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume(vol, max)
+	int	vol, max;
+{
+	return ((max * max - (max - vol) * (max - vol)) *
+		(max_volume - min_volume) / (max * max) + min_volume);
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(d, left, right)
+	struct wm_drive	*d;
+	int		left, right;
+{
+	struct cd_playback		pb;
+	struct cd_playback_status	ps;
+	struct cd_playback_control	pc;
+
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+	bzero((char *)&pb, sizeof(pb));
+	bzero((char *)&ps, sizeof(ps));
+	bzero((char *)&pc, sizeof(pc));
+
+	pb.pb_alloc_length = sizeof(ps);
+	pb.pb_buffer = (caddr_t)&ps;
+
+	if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+		return (-1);
+
+	pc.pc_chan0_select = ps.ps_chan0_select;
+	pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+		CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+		CDROM_MAX_VOLUME : left;
+	pc.pc_chan1_select = ps.ps_chan1_select;
+	pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+		CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+		CDROM_MAX_VOLUME : right;
+
+	pb.pb_alloc_length = sizeof(pc);
+	pb.pb_buffer = (caddr_t)&pc;
+
+	if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+		return (-1);
+
+	return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(d)
+	struct wm_drive	*d;
+{
+	return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0));
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(d)
+	struct wm_drive	*d;
+{
+	return (ioctl(d->fd, CDROM_RESUME_PLAY, 0));
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(d)
+	struct wm_drive *d;
+{
+	return (ioctl(d->fd, SCSI_STOP_UNIT, 0));
+}
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(d, start, end)
+	struct wm_drive	*d;
+	int		start, end;
+{
+	struct cd_play_audio_msf	msf;
+
+	msf.msf_starting_M_unit	= start / (60*75);
+	msf.msf_starting_S_unit	= (start % (60*75)) / 75;
+	msf.msf_starting_F_unit	= start % 75;
+	msf.msf_ending_M_unit	= end / (60*75);
+	msf.msf_ending_S_unit	= (end % (60*75)) / 75;
+	msf.msf_ending_F_unit	= end % 75;
+
+	if (ioctl(d->fd, SCSI_START_UNIT))
+		return (-1);
+	if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf))
+		return (-2);
+
+	return (0);
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+	/* On some systems, we can check to see if the CD is mounted. */
+	struct stat	stbuf;
+	struct ustat	ust;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+
+	return (ioctl(d->fd, CDROM_EJECT_CADDY, 0));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+	int	cd_vol, max;
+{
+	int	vol = 0, top = max, bot = 0, scaled;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol == scaled)
+			break;
+		if (cd_vol < scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(d, left, right)
+	struct wm_drive	*d;
+	int		*left, *right;
+{
+	struct cd_playback		pb;
+	struct cd_playback_status	ps;
+
+	bzero((char *)&pb, sizeof(pb));
+	bzero((char *)&ps, sizeof(ps));
+
+	pb.pb_alloc_length = sizeof(ps);
+	pb.pb_buffer = (caddr_t)&ps;
+
+	if (d->fd >= 0)
+	{
+		if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+			*left = *right = -1;
+		else
+		{
+			*left = unscale_volume(ps.ps_chan0_volume, 100);
+			*right = unscale_volume(ps.ps_chan1_volume, 100);
+		}
+	}
+	else
+		*left = *right = -1;
+
+	return (0);
+}
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi(d, cdb, cdblen, retbuf, retbuflen, getreply)
+	struct wm_drive	*d;
+	unsigned char	*cdb;
+	int		cdblen;
+	void		*retbuf;
+	int		retbuflen;
+	int		getreply;
+{
+	/* ULTRIX doesn't have a SCSI passthrough interface, does it? */
+	return (-1);
+}
+
+/*
+ * fgetline()
+ *
+ *   Simulate fgets, but joining continued lines in the output of uerf.
+ */
+
+#define BUF_SIZE        85              /* Max length of a (real) line */
+
+char *
+fgetline(fp)
+FILE    *fp;
+{
+        static char     *retval = NULL;
+        static char     holdbuf[BUF_SIZE + 1];
+        char            tmp[BUF_SIZE + 1];
+        char            *stmp;
+
+        if (!retval) {
+                retval = malloc(BUF_SIZE * 3);  /* 3 lines can be joined */
+                if (!retval)
+                        return(NULL);
+                else
+                        *retval = '\0';
+        }
+
+        if (*holdbuf) {
+                strcpy(retval, holdbuf);
+                retval[strlen(retval)-1] = '\0';
+                memset(holdbuf, 0, BUF_SIZE+1);
+        }
+        while (fgets(tmp, BUF_SIZE, fp)) {
+                stmp = tmp + strspn(tmp, " \t");
+                if (*stmp == '_') {                     /* Continuation line */
+			retval[strlen(retval)-1] = '\0';   /* Trim off C/R */
+                        strcat(retval, stmp+1);
+                } else {
+                        if (*retval) {
+                                strcpy(holdbuf, tmp);
+                                holdbuf[strlen(holdbuf)-1] = -1;
+                                return retval;
+                        } else {             /* First line read, keep reading */
+                                strcat(retval, stmp);
+                                retval[strlen(retval)-1] = '\0';
+                        }
+                }
+        }
+        return NULL;
+}
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(d)
+	struct wm_drive	*d;
+{
+	int		fd;
+	static int	warned = 0;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+
+	if (cd_device == NULL)
+		find_cdrom();
+
+	d->fd = open(cd_device, O_RDWR);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+		else if (errno != EINTR)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+	*d = *(find_drive_struct("", "", ""));
+	d->fd = fd;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif
diff --git a/ascd/libworkman/plat_sun.c b/ascd/libworkman/plat_sun.c
new file mode 100644
index 0000000..784ae5f
--- /dev/null
+++ b/ascd/libworkman/plat_sun.c
@@ -0,0 +1,1188 @@
+/*
+ * $Id: plat_sun.c,v 1.9 1999/05/28 03:35:54 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Sun-specific drive control routines.
+ */
+
+#ifdef sun
+static char plat_sun_id[] = "$Id: plat_sun.c,v 1.9 1999/05/28 03:35:54 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+
+
+#include <ustat.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef solbourne
+# include <mfg/dklabel.h>
+# include <mfg/dkio.h>
+# include <sys/unistd.h>
+# include <dev/srvar.h>
+#else /* A real Sun */
+# ifdef SYSV
+#  include <poll.h>
+#  include <stdlib.h>
+#  include <sys/cdio.h>
+#  include <sys/socket.h>
+#  include <sys/scsi/impl/uscsi.h>
+#  include "include/wm_cdda.h"
+# else
+#  include <sys/buf.h>
+#  include <sun/dkio.h>
+#  include <scsi/targets/srdef.h>
+#  include <scsi/impl/uscsi.h>
+#  include <scsi/generic/commands.h>
+# endif
+#endif
+
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+int	min_volume = 0;
+int	max_volume = 255;
+
+extern char	*cd_device, *cddaslave_path;
+extern int	intermittent_dev;
+
+int	cdda_slave = -1;
+
+int	current_end;
+
+#if defined(SYSV) && defined(SIGTHAW)
+#ifdef __GNUC__
+void sigthawinit(void) __attribute__ ((constructor));
+#else
+#pragma init(sigthawinit)
+#endif /* GNUC */
+
+static int last_left, last_right;
+static struct wm_drive *thecd;
+
+static void thawme(int sig)
+{
+	change_mode(NULL, WM_CDM_STOPPED, NULL);
+	codec_init();
+	if( thecd )
+	  gen_set_volume(thecd, last_left, last_right);
+} /* thawme */
+
+void sigthawinit( void )
+{
+	struct sigaction sa;
+
+	sa.sa_handler = thawme;
+	sigemptyset(&sa.sa_mask);
+	sa.sa_flags = 0;
+
+	sigaction(SIGTHAW, &sa, NULL);
+}
+
+#endif /* SYSV && SIGTHAW */
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0
+ * that exists.  (Check for /vol/dev/aliases, not cdrom0, since it won't be
+ * there if there's no CD in the drive.)  This is done so a single SunOS 4.x
+ * binary can be used on any 4.x or higher Sun system.
+ */
+void
+find_cdrom( void )
+{
+	if (access("/vol/dev/aliases", X_OK) == 0)
+	{
+		/* Volume manager.  Device might not be there. */
+		intermittent_dev = 1;
+
+		/* If vold is running us, it'll tell us the device name. */
+		cd_device = getenv("VOLUME_DEVICE");
+		if (cd_device == NULL)
+			cd_device = "/vol/dev/aliases/cdrom0";
+	}
+	else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0)
+	{
+		/* Solaris 2.x w/o volume manager. */
+		cd_device = "/dev/rdsk/c0t6d0s2";
+	}
+	else if (access("/dev/rcd0", F_OK) == 0)
+	{
+		cd_device = "/dev/rcd0";
+	}
+	else if (access("/dev/rsr0", F_OK) == 0)
+		cd_device = "/dev/rsr0";
+	else
+	{
+		fprintf(stderr, "Couldn't find a CD device!\n");
+		exit(1);
+	}
+}
+
+/*
+ * Wait for an acknowledgement from the CDDA slave.
+ */
+static int
+get_ack(int fd)
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	struct cdda_block	blk;
+
+	do
+		if (read(fd, &blk, sizeof(blk)) <= 0)
+			return (0);
+	while (blk.status != WMCDDA_ACK);
+#endif /* } */
+
+	return (1);
+}
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init( struct wm_drive *d )
+{
+	codec_init();
+	return (0);
+}
+
+/*
+ * Try to initialize the CDDA slave.  Returns 0 on error.
+ */
+int
+cdda_init( struct wm_drive *d )
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	int	slavefds[2];
+
+	if (cdda_slave > -1)
+		return (1);
+
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds))
+	{
+		perror("socketpair");
+		return (0);
+	}
+
+	switch (fork()) {
+	case 0:
+		close(slavefds[0]);
+		dup2(slavefds[1], 1);
+		dup2(slavefds[1], 0);
+		close(slavefds[1]);
+		close(d->fd);
+		/* Try the default path first. */
+		execl(cddaslave_path, cddaslave_path, cd_device, NULL);
+		/* Search $PATH if that didn't work. */
+		execlp("cddaslave", "cddaslave", cd_device, NULL);
+		perror(cddaslave_path);
+		exit(1);
+
+	case -1:
+		close(slavefds[0]);
+		close(slavefds[1]);
+		perror("fork");
+		return (0);
+	}
+
+	close(slavefds[1]);
+	cdda_slave = slavefds[0];
+
+	if (!get_ack(cdda_slave))
+	{
+		cdda_slave = -1;
+		codec_start();
+		return (0);
+	}
+
+	return (1);
+
+#else /* BUILD_CDDA } { */
+	/*
+	 * If we're not building CDDA support, don't even bother trying.
+	 */
+	return (0);
+#endif
+}
+
+/*
+ * Turn off the CDDA slave.
+ */
+void
+cdda_kill( struct wm_drive *d )
+{
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "Q", 1);
+		get_ack(cdda_slave);
+		wait(NULL);
+		cdda_slave = -1;
+		codec_start();
+	}
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount( struct wm_drive *d, int *tracks )
+{
+	struct cdrom_tochdr	hdr;
+
+	if (ioctl(d->fd, CDROMREADTOCHDR, &hdr))
+		return (-1);
+	
+	*tracks = hdr.cdth_trk1;
+	return (0);
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe)
+{
+	struct cdrom_tocentry	entry;
+
+	entry.cdte_track = track;
+	entry.cdte_format = CDROM_MSF;
+
+	if (ioctl(d->fd, CDROMREADTOCENTRY, &entry))
+		return (-1);
+	
+	*startframe =	entry.cdte_addr.msf.minute * 60 * 75 +
+			entry.cdte_addr.msf.second * 75 +
+			entry.cdte_addr.msf.frame;
+	*data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0;
+	
+	return (0);
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(struct wm_drive *d, int *frames )
+{
+	int		tmp;
+
+	return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames));
+}
+
+
+/* Alarm signal handler. */
+static void do_nothing( int x ) { x++; }
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status( struct wm_drive *d,
+		      enum wm_cd_modes oldmode,
+		      enum wm_cd_modes *mode,
+		      int *pos, int *track, int *index )
+{
+	struct cdrom_subchnl		sc;
+	struct itimerval		old_timer, new_timer;
+	struct sigaction		old_sig, new_sig;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	if ((oldmode == WM_CDM_PAUSED || oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_STOPPED) &&
+		cdda_slave > -1)
+	{
+		struct cdda_block	blk;
+		struct pollfd		fds;
+		int			gotone = 0;
+
+		fds.fd = cdda_slave;
+		fds.events = POLLRDNORM;
+
+		*mode = oldmode;
+
+		while (poll(&fds, 1, 0) > 0)
+		{
+			read(cdda_slave, &blk, sizeof(blk));
+			gotone = 1;
+		}
+
+		/* We only want to use the latest status report. */
+		if (gotone)
+		{
+			if (blk.status == WMCDDA_PLAYED)
+			{
+				*track = blk.track;
+				*index = blk.index;
+				*pos = blk.minute * 60 * 75 +
+					blk.second * 75 +
+					blk.frame;
+
+				*mode = WM_CDM_PLAYING;
+			}
+			else if (blk.status == WMCDDA_DONE)
+				*mode = WM_CDM_TRACK_DONE;
+			else if (blk.status == WMCDDA_STOPPED)
+			{
+				if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+					*mode = WM_CDM_PAUSED;
+				else
+					*mode = WM_CDM_STOPPED;
+			}
+			else if (blk.status == WMCDDA_ERROR)
+			{
+				/*
+				 * An error near the end of the CD probably
+				 * just means we hit the end.
+				 */
+				*mode = WM_CDM_TRACK_DONE;
+			}
+			else if (blk.status == WMCDDA_EJECTED)
+			{
+				*mode = WM_CDM_EJECTED;
+			}
+		}
+
+		return (0);
+	}
+#endif /* } */
+
+	/*
+	 * Solaris 2.2 hangs on this ioctl if someone else ejects the CD.
+	 * So we schedule a signal to break out of the hang if the call
+	 * takes an unreasonable amount of time.  The signal handler and
+	 * timer are restored immediately to avoid interfering with XView.
+	 */
+	if (intermittent_dev)
+	{
+		/*
+		 * First clear out the timer so XView's signal doesn't happen
+		 * while we're diddling with the signal handler.
+		 */
+		timerclear(&new_timer.it_interval);
+		timerclear(&new_timer.it_value);
+		setitimer(ITIMER_REAL, &new_timer, &old_timer);
+
+		/*
+		 * Now install the no-op signal handler.
+		 */
+		new_sig.sa_handler = do_nothing;
+		memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask));
+		new_sig.sa_flags = 0;
+		if (sigaction(SIGALRM, &new_sig, &old_sig))
+			perror("sigaction");
+
+		/*
+		 * And finally, set the timer.
+		 */
+		new_timer.it_value.tv_sec = 2;
+		setitimer(ITIMER_REAL, &new_timer, NULL);
+	}
+
+	sc.cdsc_format = CDROM_MSF;
+
+	if (ioctl(d->fd, CDROMSUBCHNL, &sc))
+	{
+		if (intermittent_dev)
+		{
+			sigaction(SIGALRM, &old_sig, NULL);
+			setitimer(ITIMER_REAL, &old_timer, NULL);
+
+			/* If the device can disappear, let it do so. */
+			close(d->fd);
+			d->fd = -1;
+		}
+
+		return (0);
+	}
+
+	if (intermittent_dev)
+	{
+		sigaction(SIGALRM, &old_sig, NULL);
+		setitimer(ITIMER_REAL, &old_timer, NULL);
+	}
+
+	switch (sc.cdsc_audiostatus) {
+	case CDROM_AUDIO_PLAY:
+		*mode = WM_CDM_PLAYING;
+		*track = sc.cdsc_trk;
+		*index = sc.cdsc_ind;
+		*pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+			sc.cdsc_absaddr.msf.second * 75 +
+			sc.cdsc_absaddr.msf.frame;
+		break;
+
+	case CDROM_AUDIO_PAUSED:
+	case CDROM_AUDIO_INVALID:
+	case CDROM_AUDIO_NO_STATUS:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			*track = sc.cdsc_trk;
+			*index = sc.cdsc_ind;
+			*pos = sc.cdsc_absaddr.msf.minute * 60 * 75 +
+				sc.cdsc_absaddr.msf.second * 75 +
+				sc.cdsc_absaddr.msf.frame;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	/* CD ejected manually during play. */
+	case CDROM_AUDIO_ERROR:
+		break;
+
+	case CDROM_AUDIO_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	default: 
+		*mode = WM_CDM_UNKNOWN;
+		break;
+	}
+
+	return (0);
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume( struct wm_drive *d, int left, int right )
+{
+	struct cdrom_volctrl v;
+
+#if defined(SIGTHAW) && defined(SYSV)
+	last_left = left;
+	last_right = right;
+	thecd = d;
+#endif
+	
+	if (cdda_slave > -1)
+	{
+		int		bal, vol;
+		unsigned char	cmd[2];
+
+		bal = (right - left) + 100;
+		bal *= 255;
+		bal /= 200;
+		if (right > left)
+			vol = right;
+		else
+			vol = left;
+		vol *= 255;
+		vol /= 100;
+
+		cmd[0] = 'B';
+		cmd[1] = bal;
+		write(cdda_slave, cmd, 2);
+		cmd[0] = 'V';
+		cmd[1] = vol;
+		write(cdda_slave, cmd, 2);
+		/*
+		 * Don't wait for the ack, or the user won't be able to drag
+		 * the volume slider smoothly.
+		 */
+
+		return (0);
+	}
+
+	left = (left * (max_volume - min_volume)) / 100 + min_volume;
+	right = (right * (max_volume - min_volume)) / 100 + min_volume;
+
+	v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left;
+	v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right;
+
+	return (ioctl(d->fd, CDROMVOLCTRL, &v));
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause( struct wm_drive *d )
+{
+	if (cdda_slave > -1)
+	{
+		int	dummy, mode = WM_CDM_PLAYING;
+
+		write(cdda_slave, "S", 1);
+		get_ack(cdda_slave);
+/*		while (mode != WM_CDM_PAUSED)
+			gen_get_drive_status(d, WM_CDM_PAUSED, &mode, &dummy, &dummy,
+						&dummy);
+*/
+		return (0);
+	}
+
+	codec_stop();
+	return (ioctl(d->fd, CDROMPAUSE));
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume( struct wm_drive *d )
+{
+	if (cdda_slave > -1)
+		return (1);
+
+	codec_start();
+	return (ioctl(d->fd, CDROMRESUME));
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop( struct wm_drive *d )
+{
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "S", 1);
+		get_ack(cdda_slave);
+
+		/*
+		 * The WMCDDA_STOPPED status message will be caught by
+		 * gen_get_drive_status.
+		 */
+
+		return (0);
+	}
+	codec_stop();
+	return (ioctl(d->fd, CDROMSTOP));
+}
+
+/*
+ * Play the CD from one position to another.
+ *
+ *	d		Drive structure.
+ *	start		Frame to start playing at.
+ *	end		End of this chunk.
+ *	realstart	Beginning of this chunk (<= start)
+ */
+int
+gen_play( struct wm_drive *d, int start, int end, int realstart)
+{
+	struct cdrom_msf		msf;
+	unsigned char			cmdbuf[10];
+
+	current_end = end;
+
+	if (cdda_slave > -1)
+	{
+		cmdbuf[0] = 'P';
+		cmdbuf[1] = start / (60 * 75);
+		cmdbuf[2] = (start % (60*75)) / 75;
+		cmdbuf[3] = start % 75;
+		cmdbuf[4] = end / (60*75);
+		cmdbuf[5] = (end % (60*75)) / 75;
+		cmdbuf[6] = end % 75;
+		cmdbuf[7] = realstart / (60 * 75);
+		cmdbuf[8] = (realstart % (60*75)) / 75;
+		cmdbuf[9] = realstart % 75;
+
+		/* Write the play command and make sure the slave has it. */
+		write(cdda_slave, cmdbuf, 10);
+		get_ack(cdda_slave);
+
+		return (0);
+	}
+
+	msf.cdmsf_min0 = start / (60*75);
+	msf.cdmsf_sec0 = (start % (60*75)) / 75;
+	msf.cdmsf_frame0 = start % 75;
+	msf.cdmsf_min1 = end / (60*75);
+	msf.cdmsf_sec1 = (end % (60*75)) / 75;
+	msf.cdmsf_frame1 = end % 75;
+
+	codec_start();
+	if (ioctl(d->fd, CDROMSTART))
+		return (-1);
+	if (ioctl(d->fd, CDROMPLAYMSF, &msf))
+		return (-2);
+
+	return (0);
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject( struct wm_drive *d )
+{
+	struct stat	stbuf;
+	struct ustat	ust;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "S", 1);
+		get_ack(cdda_slave);
+	}
+
+	if (ioctl(d->fd, CDROMEJECT))
+		return (-1);
+
+	/* Close the device if it needs to vanish. */
+	if (intermittent_dev)
+	{
+		close(d->fd);
+		d->fd = -1;
+		/* Also remember to tell the cddaslave since volume
+		   manager switches links around on us */
+		if (cdda_slave > -1)
+		{
+		    write(cdda_slave, "E", 1);
+		    get_ack(cdda_slave);
+		}
+	}
+
+	return (0);
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume( struct wm_drive *d, int *left, int *right )
+{
+#if defined(BUILD_CDDA) && defined(WMCDDA_DONE) /* { */
+	struct cdda_block	blk;
+
+	if (cdda_slave > -1)
+	{
+		write(cdda_slave, "G", 1);
+		get_ack(cdda_slave);
+		read(cdda_slave, &blk, sizeof(blk));
+
+		*left = *right = (blk.volume * 100 + 254) / 255;
+
+		if (blk.balance < 110)
+			*right = (((blk.volume * blk.balance + 127) / 128) *
+				  100 + 254) / 255;
+		else if (blk.balance > 146)
+			*left = (((blk.volume * (255 - blk.balance) +
+				   127) / 128) * 100 + 254) / 255;
+
+		return (0);
+	}
+#endif /* } */
+
+	*left = *right = -1;
+
+	return (wm_scsi2_get_volume(d, left, right));
+}
+
+#ifdef BUILD_CDDA /* { */
+
+/*
+ * Tell the CDDA slave to set the play direction.
+ */
+void
+gen_set_direction( int newdir )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 'd';
+		buf[1] = newdir;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to set the play speed.
+ */
+void
+gen_set_speed( int speed )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 's';
+		buf[1] = speed;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to set the loudness level.
+ */
+void
+gen_set_loudness( int loud )
+{
+	unsigned char	buf[2];
+
+	if (cdda_slave > -1)
+	{
+		buf[0] = 'L';
+		buf[1] = loud;
+		write(cdda_slave, buf, 2);
+		get_ack(cdda_slave);
+	}
+}
+
+/*
+ * Tell the CDDA slave to start (or stop) saving to a file.
+ */
+void
+gen_save( char *filename )
+{
+	int	len;
+
+	if (filename == NULL || filename[0] == '\0')
+		len = 0;
+	else
+		len = strlen(filename);
+	write(cdda_slave, "F", 1);
+	write(cdda_slave, &len, sizeof(len));
+	if (len)
+		write(cdda_slave, filename, len);
+	get_ack(cdda_slave);
+}
+
+#endif /* BUILD_CDDA } */
+
+#ifndef solbourne
+/*
+ * Send an arbitrary SCSI command out the bus and optionally wait for
+ * a reply if "retbuf" isn't NULL.
+ */
+int
+wm_scsi( struct wm_drive *d,
+	 unsigned char *cdb,
+	 int cdblen, void *retbuf,
+	 int retbuflen, int getreply )
+{
+	char			x;
+	struct uscsi_cmd	cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.uscsi_cdb = (void *) cdb;
+	cmd.uscsi_cdblen = cdblen;
+	cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x;
+	cmd.uscsi_buflen = retbuf ? retbuflen : 0;
+	cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT;
+	if (getreply)
+		cmd.uscsi_flags |= USCSI_READ;
+	
+	if (ioctl(d->fd, USCSICMD, &cmd))
+		return (-1);
+	
+	if (cmd.uscsi_status)
+		return (-1);
+	
+	return (0);
+}
+#else
+
+int wm_scsi() { return (-1); }
+
+#endif
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open( struct wm_drive *d )
+{
+	int		fd;
+	static int	warned = 0;
+	char	vendor[32] = WM_STR_GENVENDOR;
+	char	 model[32] = WM_STR_GENMODEL;
+	char	   rev[32] = WM_STR_GENREV;
+
+	if (cd_device == NULL)
+		find_cdrom();
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+	
+	d->fd = open(cd_device, 0);
+	if (d->fd < 0)
+	{
+		/* Solaris 2.2 volume manager moves links around */
+		if (errno == ENOENT && intermittent_dev)
+			return (1);
+
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				char	realname[MAXPATHLEN];
+
+				if (realpath(cd_device, realname) == NULL)
+				{
+					perror("realpath");
+					return (-1);
+				}
+
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", realname,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+		else if (errno != ENXIO)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+
+	/*
+	 * See if we can do digital audio.
+	 */
+	if (cdda_init(d))
+		enable_cdda_controls(1);
+
+	/* Can we figure out the drive type? */
+	if (wm_scsi_get_drive_type(d, vendor, model, rev)) {
+		if (errno == EPERM) {
+			/*
+			 * Solaris 2.4 seems to refuse to do USCSICMD ioctls
+			 * when not running as root.  SunOS 4.x allows it
+			 * as an unprivileged user, though.
+			 */
+			fprintf(stderr, "Warning: WorkMan can't adapt itself to your drive unless it runs as root.\n");
+		} else {
+			fprintf(stderr, "Warning: WorkMan couldn't determine drive type\n");
+		}
+		*d = *(find_drive_struct("Generic", "drive type", ""));
+	} else {
+		*d = *(find_drive_struct(vendor, model, rev));
+	}
+
+	wm_drive_settype(vendor, model, rev);
+	d->fd = fd;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+/*
+ * The following code activates the internal CD audio passthrough on
+ * SPARCstation 5 systems (and possibly others.)
+ *
+ * Thanks to <[email protected]>, Roger Oscarsson <[email protected]>
+ * and Steve McKinty <>
+ *
+ * Most CD drives have a headphone socket on the front, but it
+ * is often more convenient to route the audio though the
+ * built-in audio device. That way the user can leave their
+ * headphones plugged-in to the base system, for use with
+ * other audio stuff like ShowMeTV
+ */
+
+#ifdef CODEC /* { */
+#ifdef SYSV /* { */
+
+# include <sys/ioctl.h>
+# include <sys/audioio.h>
+# include <stdlib.h>
+
+#else /* } { */
+
+# include <sun/audioio.h>
+# define AUDIO_DEV_SS5STYLE 5
+typedef int audio_device_t;
+
+#endif /* } */
+#endif /* } */
+
+/*
+ * Don't do anything with /dev/audio if we can't set it to high quality.
+ * Also, don't do anything real if it's not Solaris.
+ */
+#if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */
+codec_init() { return 0; }
+codec_start() { return 0; }
+codec_stop() { return 0; }
+#else
+
+#ifndef AUDIO_INTERNAL_CD_IN
+#define AUDIO_INTERNAL_CD_IN	0x4
+#endif
+
+static char* devname = 0;
+static char* ctlname = 0;
+static int ctl_fd = -1;
+static int port = AUDIO_LINE_IN;
+int internal_audio = 1;
+
+codec_init( void )
+{
+    register int i;
+    char* ctlname;
+    audio_info_t foo;
+    audio_device_t aud_dev;
+
+    if (internal_audio == 0)
+    {
+        ctl_fd = -1;
+        return(0);
+    }
+    
+    if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio";
+    ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl");
+    if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0) {
+	perror(ctlname);
+	return -1;
+    }
+    if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0) {
+	close(ctl_fd);
+	ctl_fd = -1;
+	return -1;
+    }
+/*
+ * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones.
+ * Not sure if this is all correct.
+ */
+#ifdef SYSV
+    if (strcmp(aud_dev.name, "SUNW,CS4231") &&
+        strcmp(aud_dev.name, "SUNW,sb16") &&
+        strcmp(aud_dev.name, "SUNW,sbpro"))
+#else
+    if (aud_dev != AUDIO_DEV_SS5STYLE)
+#endif
+    {
+	close(ctl_fd);
+	ctl_fd = -1;
+	return 0;					/* but it's okay */
+    }
+
+/*
+ * Does the chosen device have an internal CD port?
+ * If so, use it. If not then try and use the
+ * Line In port.
+ */
+    if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+    {
+        perror("AUDIO_GETINFO");
+        close(ctl_fd);
+        ctl_fd = -1;
+        return(-1);
+    }
+    if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN)
+        port = AUDIO_INTERNAL_CD_IN;
+    else
+        port = AUDIO_LINE_IN;
+
+/*
+ * now set it up to use it. See audio(7I)
+ */
+    
+    AUDIO_INITINFO(&foo);
+    foo.record.port = port;
+    foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE;
+#ifdef BUILD_CDDA
+    if (cdda_slave > -1)
+	foo.monitor_gain = 0;
+    else
+#endif
+	foo.monitor_gain = AUDIO_MAX_GAIN;
+/*
+ * These next ones are tricky. The voulme will depend on the CD drive
+ * volume (set by the knob on the drive and/or by workman's volume
+ * control), the audio device record gain and the audio device
+ * play gain.  For simplicity we set the latter two to something
+ * reasonable, but we don't force them to be reset if the user
+ * wants to change them.
+*/
+    foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100;
+    foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100;
+    	
+    ioctl(ctl_fd, AUDIO_SETINFO, &foo);
+    return 0;
+}
+
+static kick_codec( void ) {
+    audio_info_t foo;
+    int dev_fd;
+    int retval = 0;
+
+/*
+ * Open the audio device, not the control device. This
+ * will fail if someone else has taken it.
+ */
+      
+    if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0) {
+	perror(devname);
+	return -1;
+    }
+
+    AUDIO_INITINFO(&foo);
+    foo.record.port = port;
+    foo.monitor_gain = AUDIO_MAX_GAIN;
+    
+/* These can only be set on the real device */
+    foo.play.sample_rate = 44100;
+    foo.play.channels = 2;
+    foo.play.precision = 16;
+    foo.play.encoding = AUDIO_ENCODING_LINEAR;
+    
+    if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0)
+    	perror(devname);
+
+    close(dev_fd);
+    return retval;
+}
+
+codec_start( void )
+{
+audio_info_t foo;
+
+    if (ctl_fd < 0)
+	return 0;
+
+    if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0)
+	return -1;
+
+    if (foo.play.channels != 2) return kick_codec();
+    if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+    if (foo.play.precision != 16) return kick_codec();
+    if (foo.play.sample_rate != 44100) return kick_codec();
+
+    if (foo.record.channels != 2) return kick_codec();
+    if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec();
+    if (foo.record.precision != 16) return kick_codec();
+    if (foo.record.sample_rate != 44100) return kick_codec();
+
+    if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec();
+    if (foo.record.port != port) return kick_codec();
+
+    return 0;
+}
+
+codec_stop( void ) { return 0; }
+
+#endif /* CODEC } */
+#endif /* sun } */
diff --git a/ascd/libworkman/plat_sun_audio.c b/ascd/libworkman/plat_sun_audio.c
new file mode 100644
index 0000000..7d6a4bc
--- /dev/null
+++ b/ascd/libworkman/plat_sun_audio.c
@@ -0,0 +1,489 @@
+/*
+ * $Id: plat_sun_audio.c,v 1.3 1999/03/07 08:36:41 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Sun (really Solaris) digital audio functions.
+ */
+
+#include "include/wm_config.h"
+#if defined(sun) && defined(SYSV) && defined(BUILD_CDDA) 
+
+static char plat_sun_audio_id[] = "$Id: plat_sun_audio.c,v 1.3 1999/03/07 08:36:41 dirk Exp $";
+
+#include "include/wm_cdda.h"
+
+/* types.h included by wmcdda.h */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+#include <sys/stropts.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * Since there's a lag time between writing audio to the audio device and
+ * hearing it, we need to make sure the status indicators correlate to what's
+ * playing out the speaker.  Luckily, Solaris gives us some audio
+ * synchronization facilities that make this pretty easy.
+ *
+ * We maintain a circular queue of status information.  When we write some
+ * sound to the audio device, we put its status info into the queue.  We write
+ * a marker into the audio stream; when the audio device driver encounters the
+ * marker, it increments a field in a status structure.  When we see that
+ * field go up, we grab the next status structure from the queue and send it
+ * to the parent process.
+ *
+ * The minimum size of the queue depends on the latency of the audio stream.
+ */
+#define QSIZE 500
+
+struct cdda_block	queue[QSIZE];
+int			qtail;
+int			qstart;
+
+/*
+ * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed
+ * to be playing; this is used to keep track.
+ */
+extern int playing;
+
+static int	aufd, aucfd;
+static int	raw_audio = 1;	/* Can /dev/audio take 44.1KHz stereo? */
+
+/* 
+ * For fast linear-to-ulaw mapping, we use a lookup table that's generated
+ * at startup.
+ */
+unsigned char *ulawmap, linear_to_ulaw();
+
+char *getenv();
+
+/*
+ * Dummy signal handler so writes to /dev/audio will interrupt.
+ */
+static void
+dummy( void )
+{
+	signal(SIGALRM, dummy);
+}
+
+/*
+ * Initialize the audio device.
+ */
+void
+wmaudio_init( void )
+{
+	audio_info_t		info;
+	char			*audiodev, *acdev;
+	int			linval;
+
+	audiodev = getenv("AUDIODEV");
+	if (audiodev == NULL)
+		audiodev = "/dev/audio";
+
+	acdev = malloc(strlen(audiodev) + 4);
+	if (acdev == NULL)
+	{
+		perror("Can't allocate audio control filename");
+		exit(1);
+	}
+	strcpy(acdev, audiodev);
+	strcat(acdev, "ctl");
+
+	aucfd = open(acdev, O_WRONLY, 0);
+	if (aucfd < 0)
+	{
+		perror(acdev);
+		exit(1);
+	}
+	free(acdev);
+
+	aufd = open(audiodev, O_WRONLY, 0);
+	if (aufd < 0)
+	{
+		perror(audiodev);
+		exit(1);
+	}
+
+	signal(SIGALRM, dummy);
+
+	/*
+	 * Try to set the device to CD-style audio; we can process it
+	 * with the least CPU overhead.
+	 */
+	AUDIO_INITINFO(&info);
+	info.play.sample_rate = 44100;
+	info.play.channels = 2;
+	info.play.precision = 16;
+	info.play.encoding = AUDIO_ENCODING_LINEAR;
+	info.play.pause = 0;
+	info.record.pause = 0;
+	info.monitor_gain = 0;
+
+	if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+		if (errno == EINVAL)
+		{
+			/*
+			 * Oh well, so much for that idea.
+			 */
+			AUDIO_INITINFO(&info);
+			info.play.sample_rate = 8000;
+			info.play.channels = 1;
+			info.play.precision = 8;
+			info.play.encoding = AUDIO_ENCODING_ULAW;
+			info.play.pause = 0;
+			info.record.pause = 0;
+			info.monitor_gain = 0;
+			if (ioctl(aufd, AUDIO_SETINFO, &info) < 0)
+			{
+				perror("Can't set up audio device");
+				exit(1);
+			}
+
+			/*
+			 * Initialize the linear-to-ulaw mapping table.
+			 */
+			if (ulawmap == NULL)
+				ulawmap = malloc(65536);
+			if (ulawmap == NULL)
+			{
+				perror("malloc");
+				exit(1);
+			}
+			for (linval = 0; linval < 65536; linval++)
+				ulawmap[linval] = linear_to_ulaw(linval-32768);
+			ulawmap += 32768;
+			raw_audio = 0;
+		}
+		else
+		{
+			perror(audiodev);
+			exit(1);
+		}
+}
+
+/*
+ * Get ready to play some sound.
+ */
+void
+wmaudio_ready( void )
+{
+	audio_info_t		info;
+
+	/*
+	 * Start at the correct queue position.
+	 */
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	qtail = info.play.eof % QSIZE;
+	qstart = qtail;
+
+	queue[qtail].status = WMCDDA_OK;
+}
+
+/*
+ * Stop the audio immediately.
+ */
+void
+wmaudio_stop( void )
+{
+	if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0)
+		perror("flush");
+}
+
+/*
+ * Close the audio device.
+ */
+void
+wmaudio_close( void )
+{
+	wmaudio_stop();
+	close(aufd);
+	close(aucfd);
+}
+
+/*
+ * Set the volume level.
+ */
+void
+wmaudio_volume(int level)
+{
+	audio_info_t		info;
+
+	AUDIO_INITINFO(&info);
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	info.play.gain = level;
+	if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Set the balance level.
+ */
+void
+wmaudio_balance(int level)
+{
+	audio_info_t		info;
+
+	AUDIO_INITINFO(&info);
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO");
+	level *= AUDIO_RIGHT_BALANCE;
+	info.play.balance = level / 255;
+	if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO");
+}
+
+/*
+ * Mark the most recent audio block on the queue as the last one.
+ */
+void
+wmaudio_mark_last( void )
+{
+	queue[qtail].status = WMCDDA_DONE;
+}
+
+/*
+ * Figure out the most recent status information and send it upstream.
+ */
+int
+wmaudio_send_status( void )
+{
+	audio_info_t		info;
+	int			qhead;
+
+	/*
+	 * Now send the most current status information to our parent.
+	 */
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+		perror("AUDIO_GETINFO");
+	qhead = info.play.eof % QSIZE;
+
+	if (qhead != qstart && playing)
+	{
+		int	balance;
+
+		if (queue[qhead].status != WMCDDA_DONE)
+			queue[qhead].status = WMCDDA_PLAYED;
+		queue[qhead].volume = info.play.gain;
+		queue[qhead].balance = (info.play.balance * 255) /
+					AUDIO_RIGHT_BALANCE;
+
+		send_status(queue + qhead);
+		qstart = -1;
+	}
+
+	return (queue[qhead].status == WMCDDA_DONE);
+}
+
+/*
+ * Play some audio and pass a status message upstream, if applicable.
+ * Returns 0 on success.
+ */
+int
+wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+	int			i;
+	short			*buf16;
+	int			alarmcount = 0;
+	struct itimerval	it;
+
+	alarm(1);
+
+	while (write(aufd, rawbuf, buflen) <= 0)
+		if (errno == EINTR)
+		{
+			if (! raw_audio && alarmcount++ < 5)
+			{
+				/*
+				 * 8KHz /dev/audio blocks for several seconds
+				 * waiting for its queue to drop below a low
+				 * water mark.
+				 */
+				wmaudio_send_status();
+				timerclear(&it.it_interval);
+				timerclear(&it.it_value);
+				it.it_value.tv_usec = 500000;
+				setitimer(ITIMER_REAL, &it, NULL);
+				continue;
+			}
+
+/*			close(aufd);
+			close(aucfd);
+			wmaudio_init();
+*/ wmaudio_stop();
+			alarm(2);
+			continue;
+		}
+		else
+		{
+			blk->status = WMCDDA_ERROR;
+			return (-1);
+		}
+	alarm(0);
+
+	/*
+	 * Mark this spot in the audio stream.
+	 *
+	 * Marks don't always succeed (if the audio buffer is empty
+	 * this call will block forever) so do it asynchronously.
+	 */
+	fcntl(aufd, F_SETFL, O_NONBLOCK);
+	if (write(aufd, rawbuf, 0) < 0)
+	{
+		if (errno != EAGAIN)
+			perror("audio mark");
+	}
+	else
+		qtail = (qtail + 1) % QSIZE;
+
+	fcntl(aufd, F_SETFL, 0);
+
+	queue[qtail] = *blk;
+
+	if (wmaudio_send_status() < 0)
+		return (-1);
+	else
+		return (0);
+}
+
+/*
+ * Get the current audio state.
+ */
+void
+wmaudio_state(struct cdda_block *blk)
+{
+	audio_info_t		info;
+	int			balance;
+
+	if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0)
+		perror("AUDIO_GETINFO");
+	blk->volume = info.play.gain;
+	blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE;
+}
+
+/*
+** This routine converts from linear to ulaw.
+**
+** Craig Reese: IDA/Supercomputing Research Center
+** Joe Campbell: Department of Defense
+** 29 September 1989
+**
+** References:
+** 1) CCITT Recommendation G.711  (very difficult to follow)
+** 2) "A New Digital Technique for Implementation of Any
+**     Continuous PCM Companding Law," Villeret, Michel,
+**     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
+**     1973, pg. 11.12-11.17
+** 3) MIL-STD-188-113,"Interoperability and Performance Standards
+**     for Analog-to_Digital Conversion Techniques,"
+**     17 February 1987
+**
+** Input: Signed 16 bit linear sample
+** Output: 8 bit ulaw sample
+*/
+#define ZEROTRAP    /* turn on the trap as per the MIL-STD */
+#define BIAS 0x84               /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+ 
+unsigned char
+linear_to_ulaw( sample )
+int sample;
+{
+	static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
+				   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
+				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+				   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+				   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
+	int sign, exponent, mantissa;
+	unsigned char ulawbyte;
+ 
+	/* Get the sample into sign-magnitude. */
+	sign = (sample >> 8) & 0x80;            /* set aside the sign */
+	if ( sign != 0 ) sample = -sample;              /* get magnitude */
+	if ( sample > CLIP ) sample = CLIP;             /* clip the magnitude */
+ 
+	/* Convert from 16 bit linear to ulaw. */
+	sample = sample + BIAS;
+	exponent = exp_lut[( sample >> 7 ) & 0xFF];
+	mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
+	ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
+#ifdef ZEROTRAP
+	if ( ulawbyte == 0 ) ulawbyte = 0x02;   /* optional CCITT trap */
+#endif
+ 
+	return ulawbyte;
+}
+
+/*
+ * Downsample a block of CDDA data, if necessary, for playing out an old-style
+ * audio device.
+ */
+long
+wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk)
+{
+	short		*buf16 = (short *)rawbuf;
+	int		i, j, samples;
+	int		mono_value;
+	unsigned char	*rbend = rawbuf + buflen;
+
+	/* Don't do anything if the audio device can take the raw values. */
+	if (raw_audio)
+		return (buflen);
+
+	for (i = 0; buf16 < (short *)(rbend); i++)
+	{
+		/* Downsampling to 8KHz is a little irregular. */
+		samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12;
+
+		/* And unfortunately, we don't always end on a nice boundary. */
+		if (buf16 + samples > (short *)(rbend))
+			samples = ((short *)rbend) - buf16;
+
+		/*
+		 * No need to average all the values; taking the first one
+		 * is sufficient and less CPU-intensive.  But we do need to
+		 * do both channels.
+		 */
+		mono_value = (buf16[0] + buf16[1]) / 2;
+		buf16 += samples;
+		rawbuf[i] = ulawmap[mono_value];
+	}
+
+	return (i);
+}
+
+#endif /* ... && BUILD_CDDA */
diff --git a/ascd/libworkman/plat_sun_cdda.c b/ascd/libworkman/plat_sun_cdda.c
new file mode 100644
index 0000000..4d9f758
--- /dev/null
+++ b/ascd/libworkman/plat_sun_cdda.c
@@ -0,0 +1,371 @@
+/*
+ * $Id: plat_sun_cdda.c,v 1.3 1999/03/07 08:36:41 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Sun (really Solaris) CDDA functions.
+ */
+
+#include "include/wm_config.h"
+#if defined(sun) && defined(SYSV) && defined(BUILD_CDDA) /* { */
+
+static char plat_sun_cdda_id[] = "$Id: plat_sun_cdda.c,v 1.3 1999/03/07 08:36:41 dirk Exp $";
+
+
+#include "include/wm_cdda.h"
+/* types.h and cdio.h are included by wmcdda.h */
+
+#include <stdio.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <malloc.h>
+#include <errno.h>
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+#define CDDABLKSIZE 2368
+#define SAMPLES_PER_BLK 588
+
+/* Address of next block to read. */
+int	current_position = 0;
+
+/* Address of first and last blocks to read. */
+int	starting_position = 0;
+int	ending_position = 0;
+
+/* Playback direction. */
+int	direction = 1;
+
+/* Number of blocks to read at once; initialize to the maximum. */
+/* (was 30. Set to 15 for INTeL. Maybe config option? */
+int	numblocks = 15;
+
+/*
+ * This is the fastest way to convert from BCD to 8-bit.
+ */
+unsigned char unbcd[256] = {
+  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  0,0,0,0,0,0,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,  0,0,0,0,0,0,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,  0,0,0,0,0,0,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,  0,0,0,0,0,0,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,  0,0,0,0,0,0,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,  0,0,0,0,0,0,
+ 60, 61, 62, 63, 64, 65, 66, 67, 68, 69,  0,0,0,0,0,0,
+ 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,  0,0,0,0,0,0,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,  0,0,0,0,0,0,
+ 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,  0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+
+/*
+ * Initialize the CDDA data buffer and open the appropriate device.
+ *
+ * NOTE: We allocate twice as much space as we need to actually read a block;
+ * this lets us do audio manipulations without bothering to malloc a second
+ * buffer.
+ *
+ * Also, test to see if we can actually *do* CDDA on this drive; if not, we
+ * need to exit right away so the UI doesn't show the user any CDDA controls.
+ */
+int
+wmcdda_init(char **bufadr, long *buflenadr, int init_fd, char *devname)
+{
+	struct cdrom_cdda	cdda;
+
+	*bufadr = malloc(numblocks * CDDABLKSIZE * 2);
+	if (*bufadr == NULL)
+		return (-1);
+
+	*buflenadr = numblocks * CDDABLKSIZE;
+
+	/*init_fd = open(devname, 0);
+	if (init_fd == -1)
+		init_fd = open("/dev/rdsk/c0t6d0s2", 0);
+	*/
+	init_fd = wmcdda_open(devname);
+
+	if (init_fd > -1)
+	{
+		cdda.cdda_addr = 200;
+		cdda.cdda_length = 1;
+		cdda.cdda_data = *bufadr;
+		cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+		if (ioctl(init_fd, CDROMCDDA, &cdda) < 0)
+		{
+			close(init_fd);
+			init_fd = -1;
+		}
+	}
+
+	return (init_fd);
+}
+
+/*
+ * Try to open the CD device
+ */
+int
+wmcdda_open(char *devname)
+{
+    int fd;
+
+    fd = open(devname, 0); 
+    if (fd == -1)
+	fd = open("/dev/rdsk/c0t6d0s2", 0);
+
+    return(fd);
+}
+
+
+/*
+ * Close the CD-ROM device in preparation for exiting.
+ */
+void
+wmcdda_close(int fd)
+{
+	close(fd);
+}
+
+/*
+ * Set up for playing the CD.  Actually this doesn't play a thing, just sets a
+ * couple variables so we'll know what to do when we're called.
+ */
+void
+wmcdda_setup(int start, int end, int realstart)
+{
+	current_position = start - 150;
+	ending_position = end - 150;
+	starting_position = realstart - 150;
+
+	/*
+	 * Special case: don't start at the "end" of a track if we're
+	 * playing backwards!
+	 */
+	if (direction == -1 && start == realstart)
+		current_position = ending_position - numblocks;
+}
+
+/*
+ * Read some blocks from the CD.  Stop if we hit the end of the current region.
+ *
+ * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason.
+ */
+long
+wmcdda_read(int fd, unsigned char *rawbuf, long buflen,
+	struct cdda_block *block)
+{
+	struct cdrom_cdda	cdda;
+	int			blk;
+	unsigned char		*q;
+	extern int		speed;
+
+	if ((direction > 0 && current_position >= ending_position) ||
+	    (direction < 0 && current_position < starting_position))
+	{
+		block->status = WMCDDA_DONE;
+		return (0);
+	}
+
+	cdda.cdda_addr = current_position;
+	if (ending_position && current_position + numblocks > ending_position)
+		cdda.cdda_length = ending_position - current_position;
+	else
+		cdda.cdda_length = numblocks;
+	cdda.cdda_data = rawbuf;
+	cdda.cdda_subcode = CDROM_DA_SUBQ;
+
+	if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+	{
+		if (errno == ENXIO)	/* CD ejected! */
+		{
+			block->status = WMCDDA_EJECTED;
+			return (-1);
+		}
+
+		if (current_position + numblocks > ending_position)
+		{
+			/*
+			 * Hit the end of the CD, probably.
+			 */
+			block->status = WMCDDA_DONE;
+			return (0);
+		}
+
+		/* Sometimes it fails once, dunno why */
+		if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+		{
+			if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+			{
+				if (ioctl(fd, CDROMCDDA, &cdda) < 0)
+				{
+					perror("CDROMCDDA");
+					block->status = WMCDDA_ERROR;
+					return (-1);
+				}
+			}
+		}
+	}
+
+	if (speed > 148)
+	{
+		/*
+		 * We want speed=148 to advance by cdda_length, but
+		 * speed=256 to advance cdda_length * 4.
+		 */
+		current_position = current_position +
+			(cdda.cdda_length * direction * (speed - 112)) / 36;
+	}
+	else
+		current_position = current_position +
+			cdda.cdda_length * direction;
+
+	for (blk = 0; blk < numblocks; blk++)
+	{
+		/*
+		 * New valid Q-subchannel information?  Update the block
+		 * status.
+		 */
+		q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4];
+		if (*q == 1)
+		{
+			block->status = WMCDDA_OK;
+			block->track =  unbcd[q[1]];
+			block->index =  unbcd[q[2]];
+			block->minute = unbcd[q[7]];
+			block->second = unbcd[q[8]];
+			block->frame =  unbcd[q[9]];
+		}
+	}
+
+	return (cdda.cdda_length * CDDABLKSIZE);
+}
+
+/*
+ * Normalize a bunch of CDDA data.  Basically this means ripping out the
+ * Q subchannel data and doing byte-swapping, since the CD audio is in
+ * littleendian format.
+ *
+ * Scanning is handled here too.
+ *
+ * XXX - do byte swapping on Intel boxes?
+ */
+long
+wmcdda_normalize(unsigned char *rawbuf, long buflen, struct cdda_block *block)
+{
+	int		i, nextq;
+	int		blocks = buflen / CDDABLKSIZE;
+	unsigned char	*dest = rawbuf;
+	unsigned char	tmp;
+	long		*buf32 = (long *)rawbuf, tmp32;
+
+/*
+ * this was #ifndef LITTLEENDIAN
+ * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw?
+ */
+#if WM_BIG_ENDIAN
+	if (blocks--)
+		for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+		{
+			/* Only need to use temp buffer on first block. */
+			tmp = *rawbuf++;
+			*dest++ = *rawbuf++;
+			*dest++ = tmp;
+		}
+#endif
+
+	while (blocks--)
+	{
+		/* Skip over Q data. */
+		rawbuf += 16;
+
+		for (i = 0; i < SAMPLES_PER_BLK * 2; i++)
+		{
+#if WM_LITTLE_ENDIAN
+			*dest++ = *rawbuf++;
+			*dest++ = *rawbuf++;
+#else
+			*dest++ = rawbuf[1];
+			*dest++ = rawbuf[0];
+			rawbuf += 2;
+#endif
+		}
+	}
+
+	buflen -= ((buflen / CDDABLKSIZE) * 16);
+
+	/*
+	 * Reverse the data here if we're playing backwards.
+	 * XXX - ideally this should be done above.
+	 */
+	if (direction < 0)
+	{
+		buflen /= 4;	/* we can move 32 bits at a time. */
+
+		for (i = 0; i < buflen / 2; i++)
+		{
+			tmp32 = buf32[i];
+			buf32[i] = buf32[buflen - i - 1];
+			buf32[buflen - i - 1] = tmp32;
+		}
+
+		buflen *= 4;
+	}
+
+	return (buflen);
+}
+
+/*
+ * Set the playback direction.
+ */
+void
+wmcdda_direction(int newdir)
+{
+	if (newdir == 0)
+	{
+		numblocks = 20;
+		direction = 1;
+	}
+	else
+	{
+		numblocks = 30;
+		direction = -1;
+	}
+}
+
+/*
+ * Do system-specific stuff to get ready to play at a particular speed.
+ */
+void
+wmcdda_speed(int speed)
+{
+	if (speed > 128)
+		numblocks = 12;
+	else
+		numblocks = direction > 0 ? 20 : 30;
+}
+
+#endif /* } */
diff --git a/ascd/libworkman/plat_svr4.c b/ascd/libworkman/plat_svr4.c
new file mode 100644
index 0000000..8f01739
--- /dev/null
+++ b/ascd/libworkman/plat_svr4.c
@@ -0,0 +1,458 @@
+/*
+ * $Id: plat_svr4.c,v 1.7 1999/03/07 08:36:41 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * SVR4 specific.  Much of this is similar to plat_hpux.c.
+ */
+
+#if defined(SVR4) && !defined(sun) && !defined(__sun__) && !defined(__sony_news)
+
+static char plat_svr4_id[] = "$Id: plat_svr4.c,v 1.7 1999/03/07 08:36:41 dirk Exp $";
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/mkdev.h>
+#include <sys/stat.h>
+#include <sys/sdi.h>
+#include <sys/sdi_edt.h>
+#include <sys/scsi.h>
+#include <errno.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+
+void *malloc();
+char *strchr();
+
+int	min_volume = 0;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init(d)
+	struct wm_drive	*d;
+{
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(d, tracks)
+	struct wm_drive	*d;
+	int		*tracks;
+{
+	return (wm_scsi2_get_trackcount(d, tracks));
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ */
+int
+gen_get_trackinfo(d, track, data, startframe)
+	struct wm_drive	*d;
+	int		track, *data, *startframe;
+{
+	return (wm_scsi2_get_trackinfo(d, track, data, startframe));
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(d, frames)
+	struct wm_drive	*d;
+	int		*frames;
+{
+	int		tmp;
+
+	return (wm_scsi2_get_cdlen(d, frames));
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(d, oldmode, mode, pos, track, index)
+	struct wm_drive	*d;
+	enum wm_cd_modes oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index));
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(d, left, right)
+	struct wm_drive	*d;
+	int		left, right;
+{
+	return (wm_scsi2_set_volume(d, left, right));
+}
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(d, left, right)
+	struct wm_drive	*d;
+	int		*left, *right;
+{
+	return (wm_scsi2_get_volume(d, left, right));
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(d)
+	struct wm_drive	*d;
+{
+	return (wm_scsi2_pause(d));
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(d)
+	struct wm_drive	*d;
+{
+	return (wm_scsi2_resume(d));
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(d)
+	struct wm_drive	*d;
+{
+	return (wm_scsi2_stop(d));
+}
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(d, start, end)
+	struct wm_drive	*d;
+	int		start, end;
+{
+	return (wm_scsi2_play(d, start, end));
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+	return (wm_scsi2_eject(d));
+} /* gen_eject() */
+
+/*
+ * Close the tray.
+ * please review scsi.c / wm_scsi2_closetray()
+ * and send changes to [email protected]
+ */
+int
+gen_closetray( struct wm_drive *d )
+{
+	return(wm_scsi2_closetray(d));
+} /* gen_closetray() */
+
+static int
+create_cdrom_node(char *dev_name)
+{
+	char pass_through[100];
+	int file_des;
+	dev_t pass_thru_device;
+	int err;
+	int ccode;
+
+
+	strcpy(pass_through, dev_name);
+	strcat(pass_through, "p" );
+
+	if (setreuid(-1,0) < 0)
+	{
+		perror("setregid/setreuid/access");
+		exit(1);
+	}
+
+	ccode = access(pass_through, F_OK);
+	
+	if (ccode < 0)
+	{
+		if ((file_des = open(dev_name, O_RDONLY)) < 0)
+		{
+			perror("open cdrom devices failed");
+			return -1;
+		}
+
+		if (ioctl(file_des, B_GETDEV, &pass_thru_device) < 0)
+		{
+			perror("Call to get pass-through device number failed");
+			return -1;
+		}
+
+		(void)close(file_des);
+
+		if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE),
+			pass_thru_device) < 0)
+		{
+			perror("Unable to make pass-through node");
+			return -1;
+		}
+
+        	if (chown(pass_through, 0 , 0) < 0)
+		{
+			perror("chown");
+			return -1;
+		}
+
+		if (chmod(pass_through, 0660 ) < 0)
+		{
+			perror("chmod");
+			return -1;
+		}
+	}
+	
+	file_des = open( pass_through, O_RDWR);
+	err = errno;
+
+	if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) )
+	{
+		perror("setreuid/setregid");
+		exit(1);
+	}
+	errno = err;
+	return file_des;
+}
+
+/*
+ * Open the CD and figure out which kind of drive is attached.
+ */
+int
+wmcd_open(d)
+	struct wm_drive	*d;
+{
+	int		fd, flag = 1;
+	static int	warned = 0;
+	char	vendor[32] = WM_STR_GENVENDOR;
+	char	 model[32] = WM_STR_GENMODEL;
+	char	   rev[32] = WM_STR_GENREV;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+	
+	if (cd_device == NULL)
+		cd_device = DEFAULT_CD_DEVICE;
+
+	d->fd = create_cdrom_node(cd_device); /* this will do open */
+
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (! warned)
+			{
+				fprintf(stderr,"Cannot access %s\n",cd_device);
+				warned++;
+			}
+		}
+		else if (errno != EINTR)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. (Is this true also for svr4 ? XXX ) */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+
+	fd = d->fd;
+
+	if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0)
+	{
+		perror("Cannot inquiry drive for it's type");
+		exit(1);
+	}
+	*d = *(find_drive_struct(vendor, model, rev));
+	wm_drive_settype(vendor, model, rev);
+	
+	d->fd = fd;
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+
+
+/*
+ * Send a SCSI command out the bus.
+ */
+int 
+wm_scsi(d, xcdb, cdblen, retbuf, retbuflen, getreply)
+	struct wm_drive *d;
+	unsigned char *xcdb;
+	int cdblen;
+	int getreply;
+	char *retbuf;
+	int retbuflen;
+{
+	int ccode;
+	int file_des = d->fd;
+	int i,j;
+	unsigned char sense_buffer[ SENSE_SZ ];
+	int errno_save;
+
+	/* getreply == 1 is read, == 0 is write */
+	
+	struct sb sb;
+	struct scs scs;
+
+        sb.sb_type = ISCB_TYPE;
+
+	sb.SCB.sc_comp_code = SDI_PROGRES;
+	sb.SCB.sc_int = NULL;
+	sb.SCB.sc_wd = 0;
+	sb.SCB.sc_dev.sa_major = 0;
+	sb.SCB.sc_dev.sa_minor = 0;
+	sb.SCB.sc_dev.sa_lun = 0;
+	sb.SCB.sc_dev.sa_exlun = 0;
+	sb.SCB.sc_status = 0;
+	sb.SCB.sc_link = (struct sb *) NULL;
+	sb.SCB.sc_resid = 0;
+
+	sb.SCB.sc_cmdpt = (void *)xcdb;
+	sb.SCB.sc_cmdsz = cdblen;
+
+	sb.SCB.sc_datapt = retbuf ;
+	sb.SCB.sc_datasz = retbuflen ;
+
+	if (getreply == 1)
+		sb.SCB.sc_mode = SCB_READ;
+	else
+		sb.SCB.sc_mode = SCB_WRITE;
+
+	sb.SCB.sc_time = 500;
+
+	ccode =  ioctl(file_des, SDI_SEND,  &sb);
+
+	if ( (sb.SCB.sc_comp_code != 0xd000000e ) ||
+				( sb.SCB.sc_status != 02) )
+		return ccode;
+
+	errno_save = errno;
+
+        sb.SCB.sc_comp_code = SDI_PROGRES;
+        sb.SCB.sc_int = NULL;
+        sb.SCB.sc_wd = 0;
+        sb.SCB.sc_dev.sa_major = 0;
+        sb.SCB.sc_dev.sa_minor = 0;
+        sb.SCB.sc_dev.sa_lun = 0;
+        sb.SCB.sc_dev.sa_exlun = 0;
+        sb.SCB.sc_status = 0;
+        sb.SCB.sc_link = (struct sb *) NULL;
+        sb.SCB.sc_resid = 0;
+
+	scs.ss_op	=	SS_REQSEN;
+	scs.ss_lun	=	0;
+	scs.ss_addr1	=	0;
+	scs.ss_addr	=	0;
+	scs.ss_len	=	SENSE_SZ;
+	scs.ss_cont	=	0;
+
+	sb.SCB.sc_cmdpt = SCS_AD(&scs);
+	sb.SCB.sc_cmdsz = SCS_SZ;
+	sb.SCB.sc_datapt = sense_buffer;
+	sb.SCB.sc_datasz = 18;
+	sb.SCB.sc_mode = SCB_READ;
+	sb.SCB.sc_time = 5000;
+
+	if (ioctl(file_des, SDI_SEND,  &sb) < 0)
+	{
+		fprintf(stderr,"Cannot read sense.\n");
+		exit(-1);
+	}
+
+	errno=errno_save;
+	return -1;
+}
+
+#endif
diff --git a/ascd/libworkman/plat_template.c b/ascd/libworkman/plat_template.c
new file mode 100644
index 0000000..3e7467a
--- /dev/null
+++ b/ascd/libworkman/plat_template.c
@@ -0,0 +1,269 @@
+/*
+ * $Id$
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ */
+
+
+#if [TEMPLATESYSTEM]
+
+static char plat_template_id[] = "$Id$"
+
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ * gen_init();
+ *
+ */
+
+int gen_init(struct wm_drive *d)
+{
+  return (0);
+}
+
+/*
+ * gen_get_trackcount()
+ *
+ */
+
+int gen_get_trackcount(struct wm_drive *d,int *tracks)
+{ 
+  return (0);
+}
+
+
+/*
+ * gen_get_cdlen()
+ *
+ */
+
+int gen_get_cdlen(struct wm_drive *d,int *frames)
+{
+  return (0);
+}
+
+/* 
+ * gen_get_trackinfo()
+ *
+ */
+
+int
+gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe)
+{
+  return (0);
+}
+
+/* 
+ * gen_get_drive_status()
+ *
+ */
+
+int gen_get_drive_status(struct wm_drive *d,
+			 enum wm_cd_modes oldmode,
+			 enum wm_cd_modes *mode,
+			 int *pos,
+			 int *track,
+			 int *index)
+{ 
+  return (0);
+}
+
+
+int scale_volume(int vol,int max)
+{
+  return ((vol * (max_volume - min_volume)) / max + min_volume);
+}
+
+int unscale_volume(int vol,int max)
+{
+  int n;
+  n = ( vol - min_volume ) * max_volume / (max - min_volume);
+  return (n <0)?0:n;
+}
+
+/*
+ * gen_get_volume()
+ *
+ */
+
+int gen_get_volume(struct wm_drive *d,int *left,int *right)
+{
+  return (0);
+}
+
+/* 
+ * gen_set_volume()
+ *
+ */
+
+int gen_set_volume(struct wm_drive *d,int left,int right)
+{
+  return (0);
+}
+
+/* 
+ * gen_pause()
+ *
+ */
+
+int gen_pause(struct wm_drive *d)
+{
+  return ioctl( 0 );
+}
+
+/*
+ * gen_resume
+ *
+ */
+
+int gen_resume(struct wm_drive *d)
+{
+  return (0);
+}
+
+/*
+ * gen_stop()
+ *
+ */
+
+int gen_stop(struct wm_drive *d){ 
+  return (0);
+}
+
+/*
+ * gen_play()
+ *
+ */
+
+int gen_play(struct wm_drive *d,int start,int end)
+{
+  return (0);
+}
+
+/*
+ * gen_eject()
+ *
+ */
+
+int gen_eject(struct wm_drive *d)
+{
+  return (0);
+}
+
+
+/*----------------------------------------*
+ * Close the CD tray
+ *----------------------------------------*/
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * wm_scsi()
+ *
+ */
+int wm_scsi(struct wm_drive *d,
+	    uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply)
+{
+  return (0);
+}
+
+
+/*
+ * wmcd_open()
+ *
+ */
+
+int wmcd_open(struct wm_drive *d)
+{
+  char vendor[32] = WM_STR_GENVENDOR;
+  char  model[32] = WM_STR_GENMODEL;
+  char    rev[32] = WM_STR_GENREV;
+
+  int fd;
+
+  if( ! d ){
+    errno = EFAULT;
+    return -1;
+  }
+
+  if(d->fd > -1)			/* device already open? */
+    return 0;
+
+  if( cd_device == (char *)NULL )
+    cd_device = DEFAULT_CD_DEVICE;
+
+  /* open() goes here */
+
+  *d = *(find_drive_struct(vendor, model, rev));
+  wm_drive_settype(vendor, model, rev);
+  
+  d->fd = fd;
+  d->init(d);
+
+  return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+#endif /* TEMPLATESYSTEM */
diff --git a/ascd/libworkman/plat_ultrix.c b/ascd/libworkman/plat_ultrix.c
new file mode 100644
index 0000000..d74af38
--- /dev/null
+++ b/ascd/libworkman/plat_ultrix.c
@@ -0,0 +1,666 @@
+/*
+ * $Id: plat_ultrix.c,v 1.6 1999/03/07 08:36:41 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * ULTRIX 4.2 drive control routines.
+ */
+
+#if defined(ultrix) || defined(__ultrix)
+
+static char plat_ultrix_id[] = "$Id: plat_ultrix.c,v 1.6 1999/03/07 08:36:41 dirk Exp $";
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <ustat.h>
+#include <string.h>
+#include <sys/rzdisk.h>
+#include <sys/cdrom.h>
+
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+
+#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
+
+/*
+ *   This structure will be filled with the TOC header and all entries.
+ * Ultrix doesn't seem to allow getting single TOC entries.
+ *                              - Chris Ross ([email protected])
+ */
+struct cd_toc_header_and_entries {
+	struct cd_toc_header	cdth;
+	struct cd_toc_entry		cdte[CDROM_MAX_TRACK+1];
+};
+
+void *malloc();
+char *strchr();
+
+int	min_volume = 128;
+int	max_volume = 255;
+
+extern char	*cd_device;
+
+/*
+ * find_cdrom
+ *
+ * Determine the name of the CD-ROM device.
+ *
+ * Read through the boot records (via a call to uerf) and find the SCSI
+ * address of the CD-ROM.  If the "CDROM" environment variable is set,
+ * use that instead.
+ */
+void
+find_cdrom()
+{
+	char	*data, *fgetline();
+	FILE	*uerf;
+	int	fds[2];
+	int	pid;
+
+	if ((cd_device = getenv("CDROM")) != NULL)
+		return;
+
+	pipe(fds);
+
+	if ((pid = fork()) == 0) {
+		close(fds[0]);
+		dup2(fds[1], 1);
+		execl("/etc/uerf", "uerf", "-R", "-r", "300", NULL);
+		execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", NULL);
+		_exit(1);
+	} else if (pid < 0) {
+		perror("fork");
+		exit(1);
+	}
+
+	close(fds[1]);
+	uerf = fdopen(fds[0], "r");
+
+	while (data = fgetline(uerf))
+		if (strstr(data, "RRD42")) {
+			char	*device;
+
+			cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
+			strcpy(cd_device, "/dev/r");
+			device = strstr(data, "rz");
+			device[(int)(strchr(device, ' ') - device)] = '\0';
+			strcat(cd_device, device);
+			strcat(cd_device, "c");
+			break;
+		}
+
+	fclose(uerf);
+
+	if (cd_device == NULL) {
+		fprintf(stderr,
+			"No cdrom (RRD42) is installed on this system\n");
+		exit(1);
+	}
+
+	kill(pid, 15);
+	(void)wait((int *)NULL);
+}
+
+/*
+ * Initialize the drive.  A no-op for the generic driver.
+ */
+int
+gen_init(d)
+	struct wm_drive	*d;
+{
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD.
+ */
+int
+gen_get_trackcount(d, tracks)
+	struct wm_drive	*d;
+	int		*tracks;
+{
+	struct cd_toc_header	hdr;
+
+	if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+		return (-1);
+
+	*tracks = hdr.th_ending_track;
+
+	return (0);
+}
+
+/*
+ * Get the start time and mode (data or audio) of a track.
+ *
+ * XXX - this should get cached, but that means keeping track of ejects.
+ */
+int
+gen_get_trackinfo(d, track, data, startframe)
+	struct wm_drive	*d;
+	int		track, *data, *startframe;
+{
+	struct cd_toc				toc;
+	struct cd_toc_header			hdr;
+	struct cd_toc_header_and_entries	toc_buffer;
+
+	if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
+		return (-1);
+
+	bzero((char *)&toc_buffer, sizeof(toc_buffer));
+	toc.toc_address_format = CDROM_MSF_FORMAT;
+	toc.toc_starting_track = 0;
+	toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
+					hdr.th_data_len0) & 0xfff) + 2;
+	toc.toc_buffer = (caddr_t)&toc_buffer;
+
+	if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
+		return (-1);
+
+	if (track == 0)
+		track = hdr.th_ending_track + 1;
+
+	*data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
+	*startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
+		toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
+		toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
+
+	return (0);
+}
+
+/*
+ * Get the number of frames on the CD.
+ */
+int
+gen_get_cdlen(d, frames)
+	struct wm_drive	*d;
+	int		*frames;
+{
+	int		tmp;
+
+	return (gen_get_trackinfo(d, 0, &tmp, frames));
+}
+
+/*
+ * Get the current status of the drive: the current play mode, the absolute
+ * position from start of disc (in frames), and the current track and index
+ * numbers if the CD is playing or paused.
+ */
+int
+gen_get_drive_status(d, oldmode, mode, pos, track, index)
+	struct wm_drive	*d;
+	enum wm_cd_modes	oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	struct cd_sub_channel		sc;
+	struct cd_subc_channel_data	scd;
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	sc.sch_address_format	= CDROM_MSF_FORMAT;
+	sc.sch_data_format	= CDROM_CURRENT_POSITION;
+	sc.sch_track_number	= 0;
+	sc.sch_alloc_length	= sizeof(scd);
+	sc.sch_buffer		= (caddr_t)&scd;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+		switch (wmcd_open(d)) {
+		case -1:	/* error */
+			return (-1);
+
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
+		return (0);	/* ejected */
+
+	switch (scd.scd_header.sh_audio_status) {
+	case AS_PLAY_IN_PROGRESS:
+		*mode = WM_CDM_PLAYING;
+dopos:
+		*pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
+			scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
+			scd.scd_position_data.scp_absaddr.msf.f_units;
+		*track = scd.scd_position_data.scp_track_number;
+		*index = scd.scd_position_data.scp_index_number;
+		break;
+
+	case AS_PLAY_PAUSED:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			goto dopos;
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	case AS_PLAY_COMPLETED:
+		*mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
+		break;
+
+	case AS_NO_STATUS:
+		*mode = WM_CDM_STOPPED;
+		break;
+	}
+
+	return (0);
+}
+
+/*
+ * scale_volume(vol, max)
+ *
+ * Return a volume value suitable for passing to the CD-ROM drive.  "vol"
+ * is a volume slider setting; "max" is the slider's maximum value.
+ *
+ * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
+ * increases much faster toward the top end of the volume scale than it
+ * does at the bottom.  To make up for this, we make the volume scale look
+ * sort of logarithmic (actually an upside-down inverse square curve) so
+ * that the volume value passed to the drive changes less and less as you
+ * approach the maximum slider setting.  The actual formula looks like
+ *
+ *     (max^2 - (max - vol)^2) * (max_volume - min_volume)
+ * v = --------------------------------------------------- + min_volume
+ *                           max^2
+ *
+ * If your system's volume settings aren't broken in this way, something
+ * like the following should work:
+ *
+ *	return ((vol * (max_volume - min_volume)) / max + min_volume);
+ */
+scale_volume(vol, max)
+	int	vol, max;
+{
+	return ((max * max - (max - vol) * (max - vol)) *
+		(max_volume - min_volume) / (max * max) + min_volume);
+}
+
+/*
+ * Set the volume level for the left and right channels.  Their values
+ * range from 0 to 100.
+ */
+int
+gen_set_volume(d, left, right)
+	struct wm_drive	*d;
+	int		left, right;
+{
+	struct cd_playback		pb;
+	struct cd_playback_status	ps;
+	struct cd_playback_control	pc;
+
+	left = scale_volume(left, 100);
+	right = scale_volume(right, 100);
+
+	bzero((char *)&pb, sizeof(pb));
+	bzero((char *)&ps, sizeof(ps));
+	bzero((char *)&pc, sizeof(pc));
+
+	pb.pb_alloc_length = sizeof(ps);
+	pb.pb_buffer = (caddr_t)&ps;
+
+	if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+		return (-1);
+
+	pc.pc_chan0_select = ps.ps_chan0_select;
+	pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
+		CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
+		CDROM_MAX_VOLUME : left;
+	pc.pc_chan1_select = ps.ps_chan1_select;
+	pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
+		CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
+		CDROM_MAX_VOLUME : right;
+
+	pb.pb_alloc_length = sizeof(pc);
+	pb.pb_buffer = (caddr_t)&pc;
+
+	if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
+		return (-1);
+
+	return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+gen_pause(d)
+	struct wm_drive	*d;
+{
+	return (ioctl(d->fd, CDROM_PAUSE_PLAY));
+}
+
+/*
+ * Resume playing the CD (assuming it was paused.)
+ */
+int
+gen_resume(d)
+	struct wm_drive	*d;
+{
+	return (ioctl(d->fd, CDROM_RESUME_PLAY));
+}
+
+/*
+ * Stop the CD.
+ */
+int
+gen_stop(d)
+	struct wm_drive *d;
+{
+	return (ioctl(d->fd, SCSI_STOP_UNIT));
+}
+
+/*
+ * Play the CD from one position to another (both in frames.)
+ */
+int
+gen_play(d, start, end)
+	struct wm_drive	*d;
+	int		start, end;
+{
+	struct cd_play_audio_msf	msf;
+
+	msf.msf_starting_M_unit	= start / (60*75);
+	msf.msf_starting_S_unit	= (start % (60*75)) / 75;
+	msf.msf_starting_F_unit	= start % 75;
+	msf.msf_ending_M_unit	= end / (60*75);
+	msf.msf_ending_S_unit	= (end % (60*75)) / 75;
+	msf.msf_ending_F_unit	= end % 75;
+
+	if (ioctl(d->fd, SCSI_START_UNIT))
+		return (-1);
+	if (ioctl(d->fd, CDROM_PLAY_MSF, &msf))
+		return (-2);
+
+	return (0);
+}
+
+/*
+ * Eject the current CD, if there is one.
+ */
+int
+gen_eject(struct wm_drive *d)
+{
+	/* On some systems, we can check to see if the CD is mounted. */
+	struct stat	stbuf;
+	struct ustat	ust;
+
+	if (fstat(d->fd, &stbuf) != 0)
+		return (-2);
+
+	/* Is this a mounted filesystem? */
+	if (ustat(stbuf.st_rdev, &ust) == 0)
+		return (-3);
+
+	return (ioctl(d->fd, CDROM_EJECT_CADDY));
+} /* gen_eject() */
+
+/*----------------------------------------*
+ * Close the CD tray
+ *
+ * Please edit and send changes to
+ * [email protected]
+ *----------------------------------------*/
+
+int gen_closetray(struct wm_drive *d)
+{
+#ifdef CAN_CLOSE
+	if(!close(d->fd))
+	{
+		d->fd=-1;
+		return(wmcd_reopen(d));
+	} else {
+		return(-1);
+	}
+#else
+	/* Always succeed if the drive can't close */
+	return(0);
+#endif /* CAN_CLOSE */
+} /* gen_closetray() */
+
+
+/*
+ * unscale_volume(cd_vol, max)
+ *
+ * Given a value between min_volume and max_volume, return the volume slider
+ * value needed to achieve that value.
+ *
+ * Rather than perform floating-point calculations to reverse the above
+ * formula, we simply do a binary search of scale_volume()'s return values.
+ */
+static int
+unscale_volume(cd_vol, max)
+	int	cd_vol, max;
+{
+	int	vol = 0, top = max, bot = 0, scaled;
+
+	while (bot <= top)
+	{
+		vol = (top + bot) / 2;
+		scaled = scale_volume(vol, max);
+		if (cd_vol == scaled)
+			break;
+		if (cd_vol < scaled)
+			top = vol - 1;
+		else
+			bot = vol + 1;
+	}
+	
+	if (vol < 0)
+		vol = 0;
+	else if (vol > max)
+		vol = max;
+
+	return (vol);
+}
+
+/*
+ * Read the initial volume from the drive, if available.  Each channel
+ * ranges from 0 to 100, with -1 indicating data not available.
+ */
+int
+gen_get_volume(d, left, right)
+	struct wm_drive	*d;
+	int		*left, *right;
+{
+	struct cd_playback		pb;
+	struct cd_playback_status	ps;
+
+	bzero((char *)&pb, sizeof(pb));
+	bzero((char *)&ps, sizeof(ps));
+
+	pb.pb_alloc_length = sizeof(ps);
+	pb.pb_buffer = (caddr_t)&ps;
+
+	if (d->fd >= 0)
+	{
+		if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
+			*left = *right = -1;
+		else
+		{
+			*left = unscale_volume(ps.ps_chan0_volume, 100);
+			*right = unscale_volume(ps.ps_chan1_volume, 100);
+		}
+	}
+	else
+		*left = *right = -1;
+
+	return (0);
+}
+
+/*
+ * Send an arbitrary SCSI command to a device.
+ */
+int
+wm_scsi(d, cdb, cdblen, retbuf, retbuflen, getreply)
+	struct wm_drive	*d;
+	unsigned char	*cdb;
+	int		cdblen;
+	void		*retbuf;
+	int		retbuflen;
+	int		getreply;
+{
+	/* ULTRIX doesn't have a SCSI passthrough interface, does it? */
+	return (-1);
+}
+
+/*
+ * fgetline()
+ *
+ *   Simulate fgets, but joining continued lines in the output of uerf.
+ */
+
+#define BUF_SIZE        85              /* Max length of a (real) line */
+
+char *
+fgetline(fp)
+FILE    *fp;
+{
+        static char     *retval = NULL;
+        static char     holdbuf[BUF_SIZE + 1];
+        char            tmp[BUF_SIZE + 1];
+        char            *stmp;
+
+        if (!retval) {
+                retval = malloc(BUF_SIZE * 3);  /* 3 lines can be joined */
+                if (!retval)
+                        return(NULL);
+                else
+                        *retval = '\0';
+        }
+
+        if (*holdbuf) {
+                strcpy(retval, holdbuf);
+                retval[strlen(retval)-1] = '\0';
+                memset(holdbuf, 0, BUF_SIZE+1);
+        }
+        while (fgets(tmp, BUF_SIZE, fp)) {
+                stmp = tmp + strspn(tmp, " \t");
+                if (*stmp == '_') {                     /* Continuation line */
+			retval[strlen(retval)-1] = '\0';   /* Trim off C/R */
+                        strcat(retval, stmp+1);
+                } else {
+                        if (*retval) {
+                                strcpy(holdbuf, tmp);
+                                holdbuf[strlen(holdbuf)-1] = -1;
+                                return retval;
+                        } else {             /* First line read, keep reading */
+                                strcat(retval, stmp);
+                                retval[strlen(retval)-1] = '\0';
+                        }
+                }
+        }
+        return NULL;
+}
+
+/*
+ * Open the CD device and figure out what kind of drive is attached.
+ */
+int
+wmcd_open(d)
+	struct wm_drive	*d;
+{
+	int		fd;
+	static int	warned = 0;
+
+	if (d->fd >= 0)		/* Device already open? */
+		return (0);
+
+	if (cd_device == NULL)
+		find_cdrom();
+
+	d->fd = open(cd_device, 0);
+	if (d->fd < 0)
+	{
+		if (errno == EACCES)
+		{
+			if (!warned)
+			{
+				fprintf(stderr,
+		"As root, please run\n\nchmod 666 %s\n\n%s\n", cd_device,
+		"to give yourself permission to access the CD-ROM device.");
+				warned++;
+			}
+		}
+		else if (errno != EINTR)
+		{
+			perror(cd_device);
+			exit(1);
+		}
+
+		/* No CD in drive. */
+		return (1);
+	}
+
+	if (warned)
+	{
+		warned = 0;
+		fprintf(stderr, "Thank you.\n");
+	}
+
+	/* Now fill in the relevant parts of the wm_drive structure. */
+	fd = d->fd;
+	*d = *(find_drive_struct("", "", ""));
+	d->fd = fd;
+
+	(d->init)(d);
+
+	return (0);
+} /* wmcd_open() */
+
+/*
+ * Re-Open the device if it is open.
+ */
+int
+wmcd_reopen( struct wm_drive *d )
+{
+	int status;
+
+	do {
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen ");
+		if (d->fd >= 0)		/* Device really open? */
+		{
+        	      wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closes the device and ");
+		      status = close( d->fd );   /* close it! */
+		      /* we know, that the file is closed, do we? */
+        	      d->fd = -1;
+		}
+		wm_susleep( 1000 );
+        	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n");
+		status = wmcd_open( d ); /* open it as usual */
+		wm_susleep( 1000 );
+	} while ( status != 0 );
+        return status;
+} /* wmcd_reopen() */
+
+
+
+#endif
diff --git a/ascd/libworkman/scsi.c b/ascd/libworkman/scsi.c
new file mode 100644
index 0000000..292f744
--- /dev/null
+++ b/ascd/libworkman/scsi.c
@@ -0,0 +1,531 @@
+/*
+ * $Id: scsi.c,v 1.8 1999/06/17 06:48:03 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Frontend functions for sending raw SCSI commands to the CD-ROM drive.
+ * These depend on wm_scsi(), which should be defined in each platform
+ * module.
+ */
+
+static char scsi_id[] = "$Id: scsi.c,v 1.8 1999/06/17 06:48:03 dirk Exp $";
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "include/wm_config.h"
+#include "include/wm_struct.h"
+#include "include/wm_scsi.h"
+#include "include/wm_platform.h"
+#include "include/wm_helpers.h"
+#include "include/wm_cdrom.h"
+
+#define SCMD_INQUIRY		0x12
+#define SCMD_MODE_SELECT	0x15
+#define SCMD_MODE_SENSE		0x1a
+#define SCMD_START_STOP		0x1b
+#define SCMD_PREVENT		0x1e
+#define SCMD_READ_SUBCHANNEL	0x42
+#define SCMD_READ_TOC		0x43
+#define SCMD_PLAY_AUDIO_MSF	0x47
+#define SCMD_PAUSE_RESUME	0x4b
+
+#define SUBQ_STATUS_INVALID	0x00
+#define SUBQ_STATUS_PLAY	0x11
+#define SUBQ_STATUS_PAUSE	0x12
+#define SUBQ_STATUS_DONE	0x13
+#define SUBQ_STATUS_ERROR	0x14
+#define SUBQ_STATUS_NONE	0x15
+#define SUBQ_STATUS_NO_DISC	0x17	/* Illegal, but Toshiba returns it. */
+#define SUBQ_ILLEGAL		0xff
+
+#define	PAGE_AUDIO		0x0e
+#define LEADOUT			0xaa
+
+#define WM_MSG_CLASS WM_MSG_CLASS_SCSI
+
+/*
+ * Send a SCSI command over the bus, with all the CDB bytes specified
+ * as unsigned char parameters.  This doesn't use varargs because some
+ * systems have stdargs instead and the number of bytes in a CDB is
+ * limited to 12 anyway.
+ *
+ * d	Drive structure
+ * buf	Buffer for data, both sending and receiving
+ * len	Size of buffer
+ * dir	TRUE if the command expects data to be returned in the buffer.
+ * a0-	CDB bytes.  Either 6, 10, or 12 of them, depending on the command.
+ */
+/*VARARGS4*/
+int
+sendscsi( struct wm_drive *d, void *buf,
+          unsigned int len, int dir,
+	  unsigned char a0, unsigned char a1,
+          unsigned char a2, unsigned char a3,
+          unsigned char a4, unsigned char a5,
+          unsigned char a6, unsigned char a7,
+          unsigned char a8, unsigned char a9,
+          unsigned char a10, unsigned char a11 )
+
+{
+	int		cdblen = 0;
+	unsigned char	cdb[12];
+
+	cdb[0] = a0;
+	cdb[1] = a1;
+	cdb[2] = a2;
+	cdb[3] = a3;
+	cdb[4] = a4;
+	cdb[5] = a5;
+
+	switch ((a0 >> 5) & 7) {
+	case 0:
+		cdblen = 6;
+		break;
+	
+	case 5:
+		cdb[10] = a10;
+		cdb[11] = a11;
+		cdblen = 12;
+
+	case 1:
+	case 2:
+	case 6:		/* assume 10-byte vendor-specific codes for now */
+		cdb[6] = a6;
+		cdb[7] = a7;
+		cdb[8] = a8;
+		cdb[9] = a9;
+		if (! cdblen)
+			cdblen = 10;
+		break;
+	}
+
+	return (wm_scsi(d, cdb, cdblen, buf, len, dir));
+}
+
+/*
+ * Send a MODE SENSE command and return the results (minus header cruft)
+ * in a user buffer.
+ *
+ * d	Drive structure
+ * page	Number of page to query (plus page control bits, if any)
+ * buf	Result buffer
+ */
+int
+wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf )
+{
+	unsigned char	pagebuf[255];
+	int		status, i, len, offset;
+
+	status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0,
+			page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0);
+	if (status < 0)
+		return (status);
+	
+	/*
+	 * The first byte of the returned data is the transfer length.  Then
+	 * two more bytes and the length of whatever header blocks are in
+	 * front of the page we want.
+	 */
+	len = pagebuf[0] - pagebuf[3] - 3;
+	offset = pagebuf[3] + 4;
+	for (i = 0; i < len; i++)
+		buf[i] = pagebuf[offset + i];
+
+	return (0);
+}
+
+/*
+ * Send a MODE SELECT command.
+ *
+ * d	Drive structure
+ * buf	Page buffer (no need to put on block descriptors)
+ * len	Size of page
+ */
+int
+wm_scsi_mode_select(d, buf, len)
+	struct wm_drive	*d;
+	unsigned char	*buf;
+	unsigned char	len;
+{
+	unsigned char	pagebuf[255];
+	int		i;
+
+	pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0;
+	for (i = 0; i < (int) len; i++)
+		pagebuf[i + 4] = buf[i];
+
+	return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0,
+			0, len + 4, 0,0,0,0,0,0,0));
+}
+
+/*
+ * Send an INQUIRY command to get the drive type.
+ *
+ * d		Drive structure
+ * vendor	Buffer for vendor name (8 bytes + null)
+ * model	Buffer for model name (16 bytes + null)
+ * rev		Buffer for revision level (4 bytes + null)
+ *
+ * The above string lengths apply to the SCSI INQUIRY command. The
+ * actual WorkMan drive structure reserves 31 bytes + NULL per entry.
+ *
+ * If the model name begins with "CD-ROM" and zero or more spaces, that will
+ * all be stripped off since it's just extra junk to WorkMan.
+ */
+int
+wm_scsi_get_drive_type( struct wm_drive *d, char *vendor,
+			 char *model, char *rev )
+{
+/* removed   unsigned*/
+	char		*s, *t, buf[36];
+
+	wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "Sending SCSI inquiry command...");
+	if (sendscsi(d, buf, sizeof(buf), 1, SCMD_INQUIRY, 0, 0, 0, sizeof(buf), 0,0,0,0,0,0,0))
+	  {
+		sprintf( vendor, WM_STR_GENVENDOR);
+		sprintf( model, WM_STR_GENMODEL);
+		sprintf( rev, WM_STR_GENREV);
+		wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "\nSCSI inquiry command not supported by the hardware\n");
+		return (WM_ERR_SCSI_INQUIRY_FAILED);
+	  }
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "sent.\n");
+
+	memcpy(vendor, buf + 8, 8);
+	vendor[8] = '\0';
+	memcpy(model, buf + 16, 16);
+	model[16] = '\0';
+	memcpy(rev, buf + 32, 4);
+	rev[4] = '\0';
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev);
+
+	
+	/* Remove "CD-ROM " from the model. */
+	if (! strncmp(model, "CD-ROM", 6))
+	{
+		s = model + 6;
+		t = model;
+		while (*s == ' ' || *s == '\t')
+			s++;
+		while( (*t++ = *s++) )
+			;
+	}
+	wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "Cooked data: %s %s rev. %s\n", vendor, model,rev);
+	return (0);
+} /* wm_scsi_get_drive_type() */
+
+/*
+ * Send a SCSI-2 PAUSE/RESUME command.  "resume" is 1 to resume, 0 to pause.
+ */
+int
+wm_scsi2_pause_resume(d, resume)
+	struct wm_drive	*d;
+	int		resume;
+{
+	return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0,
+			0, resume ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 "prevent media removal" command.  "prevent" is 1 to lock
+ * caddy in.
+ */
+int
+wm_scsi2_prevent(d, prevent)
+	struct wm_drive	*d;
+	int		prevent;
+{
+	return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0,
+			0, prevent ? 1 : 0, 0,0,0));
+}
+
+/*
+ * Send a SCSI-2 PLAY AUDIO MSF command.  Pass the starting and ending
+ * frame numbers.
+ */
+int
+wm_scsi2_play(d, sframe, eframe)
+	struct wm_drive	*d;
+	int		sframe, eframe;
+{
+	return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0,
+			sframe / (60 * 75), (sframe / 75) % 60, sframe % 75,
+			eframe / (60 * 75), (eframe / 75) % 60, eframe % 75,
+			0,0,0));
+}
+
+/*
+ * Send a SCSI-2 READ TOC command to get the data for a particular track.
+ * Fill in track information from the returned data.
+ */
+int
+wm_scsi2_get_trackinfo(d, track, data, startframe)
+	struct wm_drive	*d;
+	int		track, *data, *startframe;
+{
+	unsigned char	buf[12];	/* one track's worth of info */
+
+	if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2,
+			0, 0, 0, 0, track, sizeof(buf) / 256,
+			sizeof(buf) % 256, 0,0,0))
+		return (-1);
+	
+	*data = buf[5] & 4 ? 1 : 0;
+	*startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+
+	return (0);
+}
+
+/*
+ * Get the starting frame for the leadout area (which should be the same as
+ * the length of the disc as far as WorkMan is concerned).
+ */
+int
+wm_scsi2_get_cdlen(d, frames)
+	struct wm_drive	*d;
+	int		*frames;
+{
+	int		tmp;
+
+	return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, frames));
+}
+
+/*
+ * Get the current status of the drive by sending the appropriate SCSI-2
+ * READ SUB-CHANNEL command.
+ */
+int
+wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index)
+	struct wm_drive	*d;
+	enum wm_cd_modes oldmode, *mode;
+	int		*pos, *track, *index;
+{
+	unsigned char	buf[48];
+
+	/* If we can't get status, the CD is ejected, so default to that. */
+	*mode = WM_CDM_EJECTED;
+
+	/* Is the device open? */
+	if (d->fd < 0)
+	{
+/*
+ * stupid somehow, but necessary this time
+ */		
+		switch( wmcd_open( d ) ) {
+
+		case -1:	/* error */
+			return (-1);
+		
+		case 1:		/* retry */
+			return (0);
+		}
+	}
+
+	/* If we can't read status, the CD has been ejected. */
+	buf[1] = SUBQ_ILLEGAL;
+	if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1,
+			0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0))
+		return (0);
+	
+        switch (buf[1]) {
+        case SUBQ_STATUS_PLAY:
+		*mode = WM_CDM_PLAYING;
+		*track = buf[6];
+		*index = buf[7];
+		*pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11];
+		break;
+
+	case SUBQ_STATUS_PAUSE:
+		if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
+		{
+			*mode = WM_CDM_PAUSED;
+			*track = buf[6];
+			*index = buf[7];
+			*pos = buf[9] * 60 * 75 +
+				buf[10] * 75 +
+				buf[11];
+		}
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	/*
+	 * SUBQ_STATUS_DONE is sometimes returned when the CD is idle,
+	 * even though the spec says it should only be returned when an
+	 * audio play operation finishes.
+	 */
+	case SUBQ_STATUS_DONE:
+	case SUBQ_STATUS_NONE:
+	case SUBQ_STATUS_INVALID:
+		if (oldmode == WM_CDM_PLAYING)
+			*mode = WM_CDM_TRACK_DONE;
+		else
+			*mode = WM_CDM_STOPPED;
+		break;
+
+	/*
+	 * This usually means there's no disc in the drive.
+	 */
+	case SUBQ_STATUS_NO_DISC:
+		break;
+
+	/*
+	 * This usually means the user ejected the CD manually.
+	 */
+	case SUBQ_STATUS_ERROR:
+		break;
+
+	case SUBQ_ILLEGAL:	/* call didn't really succeed */
+		break;
+
+        default:
+		*mode = WM_CDM_UNKNOWN;
+#ifdef DEBUG		
+		if( getenv( "WORKMAN_DEBUG" ) != NULL )
+			printf("wm_scsi2_get_drive_status: status is 0x%x\n",
+				buf[1]);
+#endif				
+		break;
+        }
+
+	return (0);
+}
+
+/*
+ * Get the number of tracks on the CD using the SCSI-2 READ TOC command.
+ */
+int
+wm_scsi2_get_trackcount(d, tracks)
+	struct wm_drive	*d;
+	int		*tracks;
+{
+	unsigned char	buf[4];
+
+	if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0,
+			0, 0, 0, 0, 0, sizeof(buf) / 256,
+			sizeof(buf) % 256, 0,0,0))
+		return (-1);
+
+	*tracks = buf[3] - buf[2] + 1;
+	return (0);
+}
+
+/*
+ * Pause the CD.
+ */
+int
+wm_scsi2_pause(d)
+	struct wm_drive	*d;
+{
+	return (wm_scsi2_pause_resume(d, 0));
+}
+
+/*
+ * Resume playing after a pause.
+ */
+int
+wm_scsi2_resume(d)
+	struct wm_drive	*d;
+{
+	return (wm_scsi2_pause_resume(d, 1));
+}
+
+/*
+ * Stop playing the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_stop(d)
+	struct wm_drive	*d;
+{
+	return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Eject the CD by sending a START STOP UNIT command.
+ */
+int
+wm_scsi2_eject(struct wm_drive *d)
+{
+	/* Unlock the disc (possibly unnecessary). */
+	if (wm_scsi2_prevent(d, 0))
+		return (-1);
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Issuing START_STOP for ejecting...\n");
+	return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Something like a dummy. The SCSI-2 specs are too hard for me to
+ * understand here...
+ *
+ * If you have the correct command handy, please send the code to
+ * [email protected]
+ */
+int
+wm_scsi2_closetray(struct wm_drive *d)
+{
+	wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Issuing START_STOP for closing...\n");
+	return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0));
+}
+
+/*
+ * Get the volume by doing a MODE SENSE command.
+ */
+int
+wm_scsi2_get_volume(d, left, right)
+	struct wm_drive	*d;
+	int		*left, *right;
+{
+	unsigned char	mode[16];
+
+	*left = *right = -1;
+
+	/* Get the current audio parameters first. */
+	if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+		return (-1);
+
+	*left = ((int) mode[9] * 100) / 255;
+	*right = ((int) mode[11] * 100) / 255;
+
+	return (0);
+}
+
+/*
+ * Set the volume by doing a MODE SELECT command.
+ */
+int
+wm_scsi2_set_volume(d, left, right)
+	struct wm_drive	*d;
+	int		left, right;
+{
+	unsigned char	mode[16];
+
+	/* Get the current audio parameters first. */
+	if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode))
+		return (-1);
+	
+	/* Tweak the volume part of the parameters. */
+	mode[9] = (left * 255) / 100;
+	mode[11] = (right * 255) / 100;
+
+	/* And send them back to the drive. */
+	return (wm_scsi_mode_select(d, mode, sizeof(mode)));
+}
diff --git a/ascd/libworkman/wm_helpers.c b/ascd/libworkman/wm_helpers.c
new file mode 100644
index 0000000..c36e464
--- /dev/null
+++ b/ascd/libworkman/wm_helpers.c
@@ -0,0 +1,229 @@
+/*
+ * $Id: wm_helpers.c,v 1.7 1999/03/07 08:36:41 dirk Exp $
+ *
+ * This file is part of WorkMan, the civilized CD player library
+ * (c) 1991-1997 by Steven Grimm (original author)
+ * (c) by Dirk Försterling (current 'author' = maintainer)
+ * The maintainer can be contacted by his e-mail address:
+ * [email protected] 
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * Some helpful functions...
+ *
+ */
+
+static char wm_helpers_id[] = "$Id: wm_helpers.c,v 1.7 1999/03/07 08:36:41 dirk Exp $";
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include "include/workman_defs.h"
+#include "include/wm_config.h"
+#include "include/wm_helpers.h"
+#include "include/wm_struct.h"
+
+int wm_lib_verbosity = WM_MSG_LEVEL_NONE;
+
+/* 
+ * Some seleced functions of version reporting follow...
+ */
+
+int wm_libver_major( void ){return WM_LIBVER_MAJOR;}
+int wm_libver_minor( void ){return WM_LIBVER_MINOR;}
+int wm_libver_pl( void ){return WM_LIBVER_PL;}
+
+char *wm_libver_name( void )
+{
+	char *s = NULL;
+	
+	wm_strmcat(&s, WM_LIBVER_NAME);
+	return s;
+} /* wm_libver_name() */
+
+char *wm_libver_number( void )
+{
+	char *s = NULL;
+
+	s = malloc(10);
+	/* this is not used very often, so don't care about speed...*/
+	sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl());
+	return s;
+} /* wm_libver_number() */
+
+char *wm_libver_date( void )
+{
+	char *s = NULL;
+	wm_strmcat(&s, __DATE__);
+	return s;
+} /* wm_libver_date() */
+
+char *wm_libver_string( void )
+{
+	char *s = NULL;
+
+	wm_strmcat( &s, wm_libver_name() ); 
+	wm_strmcat( &s, " " ); 
+	wm_strmcat( &s, wm_libver_number() ); 
+	return s;
+} /* wm_libver_string() */
+
+
+/*
+ *
+ * Now for some memory management...
+ *
+ */
+
+/* Free some memory and set a pointer to null. */
+void freeup( char **x )
+{
+	if (*x != NULL)
+	{
+		free(*x);
+		*x = NULL;
+	}
+} /* freeup() */
+
+/* Copy into a malloced string. */
+void
+wm_strmcpy( char **t, char *s )
+{
+	if (*t != NULL)
+		free(*t);
+
+	*t = malloc(strlen(s) + 1);
+	if (*t == NULL)
+	{
+		perror("wm_strmcpy");
+		exit(1);
+	}
+
+	(void) strcpy(*t, s);
+} /* wm_strmcpy() */
+
+/* Add to a malloced string. */
+void
+wm_strmcat( char **t, char *s)
+{
+	int	len = strlen(s) + 1;
+
+	if (*s == '\0')
+		return;
+
+	if (*t != NULL)
+	{
+		len += strlen(*t);
+		*t = realloc(*t, len);
+		if (*t == NULL)
+		{
+			perror("strmcat");
+			exit(1);
+		}
+		strcat(*t, s);
+	}
+	else
+		wm_strmcpy(t, s);
+} /* wm_strmcat() */
+
+/* Duplicate a string.  Some systems have this in libc, but not all. */
+char *
+wm_strdup( char *s )
+{
+	char	*new;
+
+	new = malloc(strlen(s) + 1);
+	if (new)
+		strcpy(new, s);
+	return (new);
+} /* wm_strdup() */
+
+
+/*
+ * set and get verbosity level.
+ */
+void wm_lib_set_verbosity( int level )
+{
+	if( WM_MSG_LEVEL_NONE <= level <= WM_MSG_LEVEL_DEBUG )
+	{
+	 	wm_lib_verbosity = level;
+	}
+} /* wm_lib_set_verbosity */
+
+int wm_lib_get_verbosity( void )
+{
+	return wm_lib_verbosity;
+}
+
+/*
+ * wm_lib_message().
+ *
+ * any message that falls into allowed classes and has at least
+ * verbosity level wm_lib_verbosity & 0xf will be printed.
+ *
+ * Usage:
+ *
+ * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents);
+ *
+ * To simplify the usage, you may simply use WM_MSG_CLASS. It should be
+ * defined in each module to reflect the correct message class.
+ *
+ */
+void wm_lib_message( unsigned int level, char *fmt, ... )
+{
+	va_list ap;
+	/* verbosity level */
+	unsigned int vlevel = wm_lib_verbosity & 0xf;
+	/* allowed classes */
+	unsigned int vclass = (level & WM_MSG_CLASS_ALL) & (wm_lib_verbosity & WM_MSG_CLASS_ALL);
+
+	/*
+         * just give me the level
+         */
+	level &= 0xf;
+	if(level <= WM_MSG_LEVEL_NONE)
+	{
+		fprintf(stderr, "LibWorkMan warning: A LibWorkMan programmer specified an invalid message level.\n");
+	}
+        /*
+         * print it only if level and class are allowed.
+         */	
+	if( (level <= vlevel) && (vclass != 0) )
+	{
+		va_start(ap, fmt);
+		vfprintf(stderr, fmt, ap);
+		va_end(ap);
+	}
+} /* wm_lib_message() */
+
+/*
+ * Simulate usleep() using select().
+ */
+int
+wm_susleep( int usec )
+{
+	struct timeval	tv;
+
+	timerclear(&tv);
+	tv.tv_sec = usec / 1000000;
+	tv.tv_usec = usec % 1000000;
+	return (select(0, NULL, NULL, NULL, &tv));
+} /* wm_susleep() */
+
+
diff --git a/ascd/version.h b/ascd/version.h
new file mode 100644
index 0000000..d651757
--- /dev/null
+++ b/ascd/version.h
@@ -0,0 +1 @@
+#define VERSION 		"0.13.2"
-- 
2.7.4


-- 
To unsubscribe, send mail to [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.