shithub: plan9front

Download patch

ref: c613382caf1c0bffa38dc09f113d8c11dbc47628
parent: c6cdee420da18fc02c44c257505f3c43a331c7a4
author: Sigrid <ftrvxmtrx@gmail.com>
date: Tue Apr 13 07:25:24 EDT 2021

remove juke (use play or zuke instead)

ape/diff: a/sys/src/games/music/icon/null: No such file or directory ape/diff: a/sys/src/games/music/jukebox/null: No such file or directory ape/diff: a/sys/src/games/music/jukefs/null: No such file or directory ape/diff: a/sys/src/games/music/playlistfs/null: No such file or directory ape/diff: a/sys/src/games/music/null: No such file or directory
--- a/rc/bin/juke	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,57 +0,0 @@
-#!/bin/rc
-rfork e
-wide=`{echo $vgasize | sed 's/(.*)x.*x.*/\1 > 240/' | hoc}
-debug=0
-tflag=''
-wflag=''
-host=''
-flags=()
-sname=$user
-if (! ~ $wide 1) {
-	flags=($flags -t)
-}
-while(! ~ $#* 0) {
-	switch ($1) {
-	case -d
-		debug=$2
-		shift
-	case -t
-		tflag='-t'
-	case -h
-		host=$2
-		shift
-	case -w
-		wflags='-w'
-	case -s
-		sname=$2
-		shift
-	case -*
-		echo usage: juke [-d level] [-tw] [-s srv] [-h srvhost] >[1=2]
-		exit usage
-	}
-	shift
-}
-if (! test -f /mnt/playlist) {
-	if (! ~ $debug '0') echo mounting playlistfs
-	if (! test -e /srv/playlist.$sname && ! ~ $host ''){
-		import -a $host /srv /srv
-	}
-	if (! mount -b /srv/playlist.$sname /mnt >/dev/null >[2]/dev/null){
-		rm -f /srv/playlist.$sname
-		if (! ~ $debug '0') echo starting playlistfs
-		games/playlistfs -s $sname -d $debug
-	}
-}
-if (~ `{ls /mnt/juke >[2]/dev/null | sed '1q'} '') {
-	if (! test -e /srv/jukefs.$sname && ! ~ $host ''){
-		import -a $host /srv /srv
-	}
-	if (! mount -b /srv/jukefs.$sname /mnt >/dev/null >[2]/dev/null){
-		if (! ~ $debug '0') echo games/jukefs
-		games/jukefs -s $sname
-	}
-}
-if (~ $wflags '-w') {
-	exec games/jukebox -w -d $debug $tflag &
-}
-exec games/jukebox -d $debug $tflag
--- a/sys/man/7/juke	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,368 +0,0 @@
-.TH JUKE 7
-.SH NAME
-juke \- music jukebox
-.SH SYNOPSIS
-.B juke
-[
-.B \-t
-]
-[
-.B \-w
-]
-[
-.B \-h
-.I srvhost
-]
-[
-.B \-s
-.I srvname
-]
-.ift .sp 0.5
-.ifn .sp
-.B games/jukebox
-[
-.B \-t
-]
-[
-.B \-w
-]
-.ift .sp 0.5
-.ifn .sp
-.B games/jukefs
-[
-.B \-m
-.I mountpoint
-]
-[
-.B \-s
-.I srvname
-]
-[
-.I mapfile
-]
-.SH DESCRIPTION
-.I Jukebox
-controls a playlist server
-(see
-.IR playlistfs (7))
-through a graphical user interface.  It connects to a music database server which reads a set of
-.I map
-files that describe recordings and their location.  Currently, there is
-one set of maps, mostly for classical music, with some jazz and other stuff
-thrown in.  These are served by
-.BR jukefs ,
-which presents a file system conventionally mounted at
-.BR /mnt/juke .
-The playlist, explained below, is managed by a file system implemented by
-.IR playlistfs (7)
-and normally mounted on
-.BR /mnt .
-.PP
-.I Jukebox
-is most easily started through the
-.I juke
-shell script.
-.PP
-.I Jukebox
-has four windows, which can be selected by clicking the appropriate tab
-at the top of the window.
-.PP
-Above the tab are nine buttons and a volume slider.  The
-.ift buttons, shown below,
-.ifn buttons
-are named, from left to right,
-.IR Exit ,
-.IR Pause ,
-.IR Play ,
-.IR Halt ,
-.IR Back ,
-.IR Forward ,
-.IR Root ,
-.IR Delete ,
-and
-.IR Help .
-The buttons are
-.I active
-when they are displayed in dark green (or red).  When they are pale blue
-they are
-.IR inactive .
-The Exit button is always active; it exits the program (but leaves the playlist and music database
-servers running).
-.PP
-The
-.I browse
-window is for browsing through the music and selecting music to play.
-Browsing down in the music hierarchy is done by clicking button one on
-an item.  Clicking button three goes back up.
-Clicking button two recursively adds all files below the selected item to
-the
-.IR "play list" .
-.PP
-The selected music is displayed in the
-.I playlist 
-window.
-The track currently playing is shown in the
-.I playing
-window.
-.PP
-The
-.I Root
-button browses back to the root.
-.PP
-The
-.I Delete
-button empties the playlist.
-.PP
-The
-.I Help
-displays a minimal on-line manual.
-.PP
-.I Play
-starts playing at the beginning of the play list, or at the selected track in
-the play list.
-.PP
-During play,
-.IR Pause ,
-.IR Stop ,
-.IR Back ,
-and
-.I Forward
-are active.
-.I Back
-and
-.I Forward
-go back or forward a track at a time.  The other buttons do the obvious thing.
-.PP
-The
-.B \-t
-flag chooses a tiny font, useful for handhelds.
-.PP
-The
-.B \-w
-flag creates the jukebox in a new window.  Normally, the jukebox takes over
-the window in which it is invoked.
-.PP
-The
-.B \-s
-flag specifies the name under which the file descriptors of the playlist and databse servers are posted
-in /srv.  This allows two or more play list servers to exist on one platform, e.g., when
-there are several audio devices.  The default value of the flag is
-.B $\f2user\fP
-for a playlist server at
-.B /srv/playlistfs.$\f2user\fP
-and a database server at
-.BR /srv/jukefs.$\f2user\fP .
-.sp
-.LP
-.B Jukefs
-reads a set of
-.I maps
-describing the music data, builds an in-memory database, and provides
-lookup service to
-.IR jukebox .
-The default map is
-.BR /sys/lib/music/map .
-It consists of a hierarchical set of
-.IR objects .
-Each object has a type, a value, zero or more attribute-value
-pairs and zero or more subobjects.    An object consists of the
-type, followed by its contents between curly brackets.
-Attribute value pairs consist
-of a single line containing an attribute name, an equals sign, and
-a value.
-The value of an object is any text not containing curly brackets or equals
-signs.  Here is an example:
-.EX
-.ps -2
-.vs -2p
-.sp
-category {
-	composer = mahler
-
-	Gustav Mahler
-	(1860 — 1911)
-
-	work {
-		path {classic/mahler}
-		class = symphonic
-		orchestra = rfo
-		conductor = Waart,~Edo~de
-
-		Symphony Nº 5 in c♯ (RFO, Vienna)
-		performance{
-			Radio Filharmonisch Orkest Holland
-			Edo de Waart, conductor
-
-			recorded: Musikverein, Vienna, May 6, 1996
-		}
-		command {number}
-		track {
-			Trauermarsch (In gemessenem Schritt. Streng. Wie ein Kondukt)
-			time {13:55}
-			file {034.pac}
-		}
-		track {
-			Stürmisch bewegt, mit größter Vehemenz
-			time {15:34}
-			file {035.pac}
-		}
-		track {
-			Scherzo (Kräftig, nicht zu schnell)
-			time {18:54}
-			file {036.pac}
-		}
-		track {
-			Adagietto (Sehr Langsam)
-			time {10:01}
-			file {037.pac}
-		}
-		track {
-			Rondo–Finale (Allegro)
-			time {15:44}
-			file {038.pac}
-		}
-	}
-}
-.EE
-.LP
-This example shows a
-.I category
-object for the composer Gustav Mahler (the value consists of the two
-lines `Gustav Mahler' and `(1860 — 1911)') with one subobject, a
-.I work
-object whose value is `Symphony Nº 5 in c♯ (RFO, Vienna)'.  The work object
-contains six subobjects: one
-.I performance
-object and five
-.I track
-objects.
-.PP
-.I Category
-objects must contain exactly one attribute-value pair.  The attribute
-names a subobject of the root under which this category object will
-be placed.  Gustav Mahler, thus, will be placed in
-Root→composer.
-.IR Work ,
-.IR Recording ,
-.IR Part ,
-and
-.IR Track ,
-objects all describe named containers for subunits.
-A
-.IR Lyrics ,
-.IR Performance ,
-or
-.IR Soloists
-object adds information to a
-.IR Work ,
-.IR Recording ,
-.IR Part ,
-or
-.IR Track ,
-object.  It should only contain text.
-The same is true for a
-.I Time
-object; however, it should only be used adjacent to
-.I File
-objects and it should contain the running time of that file (this
-is for future use).
-.PP
-A
-.I File
-object specifies a file to be played.  When the
-.I Select
-button is pressed, all file objects contained hierarchically in the
-selected object are added to the playlist.
-.PP
-There are a number of pseudo objects:
-.I Command
-may contain either
-.I sort
-or
-.IR number .
-The
-.I sort
-command sorts the subobjects of the object it appears in by
-.I key
-or textual content.
-The
-.I number
-commands prepends numbers to the texts of its subobjects
-(e.g., for the parts in a symphony)
-.PP
-An
-.I Include
-object is replaced by the contents of the named file.
-.PP
-A
-.I Key
-object specifies a key for sorting subobjects.
-.PP
-Finally, a
-.I Path
-object specifies a path to be prepended to the files named in
-hierarchically contained
-.I File
-objects.
-.PP
-The attribute-value value pairs arrange for entries to be made of the
-current object in a
-.I Category
-object named by the attribute directly under the root.
-.sp
-.LP
-The interface to the browsing database is through a file system
-implemented by
-.BR jukefs .
-The file system synthesises a directory per object.  Each directory contains a set of files
-describing the object's attributes:
-.TP
-.B children
-contains a new-line separated list of subobject names.  For each name,
-.I x
-the directory
-.BI /mnt/juke/ x
-describes the subobject.
-.TP
-.B digest
-contains a one-line summary of the object
-.TP
-.B files
-is a new-line separated list of file objects contained in this object.
-Each line consists of object name and file name.
-.TP
-.B fulltext
-is the fulltextual value of the object.
-.TP
-.B key
-contains the key by which objects are sorted
-.TP
-.B miniparentage
-is a one-line summary of the objects and the path leading to it from the root.
-This is the line displayed in the playlist and bottom browse windows of
-.BR games/jukebox .
-.TP
-.B parent
-is the object reference to the parent of this object.
-.TP
-.B parentage
-is a full description of the path leading to this object and the object itself.
-This is the string displayed in the top of the Browse and Playing windows
-of
-.BR games/jukebox .
-.TP
-.B text
-is the text field of the object.
-.TP
-.B type
-is the type of the object
-.LP
-.SH FILES
-.BR /sys/lib/music/map :
-Default map file
-.BR /mnt/juke :
-Default mount point for the music database.
-.SH SOURCE
-.B /sys/src/games/music
-.SH SEE ALSO
-.IR playlistfs (7).
--- a/sys/man/7/playlistfs	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,147 +0,0 @@
-.TH PLAYLISTFS 7
-.SH NAME
-playlistfs \- playlist file system
-.SH SYNOPSIS
-.B games/playlistfs
-[
-.B \-s
-.I postname
-]
-[
-.B \-m
-.I mountpoint
-]
-[
-.B \-a
-]
-.SH DESCRIPTION
-.B Playlistfs
-implements an audio player which plays files from a built-in play list.
-The player is controlled through three files, usually mounted at
-.BR /mnt .
-The files are
-.B /playctl
-for controlling play: start, stop, pause, skip, etc.;
-.B /playvol
-for controlling the playout volume; and
-.B /playlist
-for controlling the play list itself.
-.PP
-All three files can be written to control the player and read to obtain player
-status information.
-.PP
-When read, the files report the current status of the player, volume and playlist,
-respectively.  End of file is indicated by a read that returns zero bytes, as usual.
-However, in all three files, subsequent read operations will block until the status
-of the file changes and then report the changed state.  When the changed state has
-been read, another end-of-file indication is given, after which another read
-can be issued to wait for state changes.
-.PP
-The
-.B /playctl
-file returns strings of the form `\f2cmd n\fP'
-where
-.I cmd
-is one of
-.IR stop ,
-.IR pause ,
-or
-.I play
-and
-.I n
-is an index (or offset) into the playlist; indices start at zero.
-.PP
-The commands that can be written to
-.B /playctl
-take the same form; however, the index is an optional argument.  If the
-index is omitted, the current value is used. The commands are
-.IR play ,
-.IR stop ,
-.IR pause ,
-.IR resume ,
-and
-.IR skip .
-.I Play
-starts playing at the index.
-.I Stop
-stops playing.  If an index is given, the current index is set to it and
-can be used in future commands.
-.I Pause
-and
-.I Resume
-interrupt and continue play, respectively.  The index argument is always ignored and
-the whole command is ignored if the state in which they occur does not
-make sense.
-.I Skip
-adds the argument to the current index (adds one if no argument is given)
-and starts play at that index, stopping current play, if necessary.
-.PP
-Reads of
-.B /playvol
-return strings of the form
-.BR "`volume \f2n\fP'" ,
-where
-.I n
-is a number or, if there is more than one channel, a quoted set of numbers, between 0
-(minimum) and 100 (maximum).
-Writes to
-.B /playvol
-take the same form.
-.PP
-The
-.B /playlist
-file is an append-only file which accepts lines with one or two fields
-per line (parsed using
-.BR tokenize ).
-The first, compulsory, field is a file name, the optional second argument
-may contain a reference to, or a description of, the item, for instance in a graphical
-user interface.
-.B /playlist
-is append-only, individual lines cannot be removed.  However, the playlist
-can be cleared by opening the file with the
-.B OTRUNC
-flag.  A process that has
-.B /playlist
-open while the file is truncated will receive an error on the next read with
-.B errstr
-set to
-.IR "reading past eof" .
-When this error occurs, clients can seek to the beginning of the file and reread its contents.
-.PP
-After starting up,
-.B Playlistfs
-puts itself in the background. When called with the
-.B \-s
-flag, it posts a mountable file descriptor in
-.BR /srv/playlist.\f2postname\fP .
-The
-.B \-m
-flag can be used to specify a mount point other than
-.BR /mnt .
-.PP
-.B Playlistfs
-uses the
-.IR audio (1)
-decoders by running
-.IR play (1)
-for format detection and conversion to pcm.
-.SH FILES
-.BR /srv/playlistfs.\f2user\fP :
-default
-.B playlistfs
-mountable file descriptor used by juke(7).
-.br
-.BR /mnt/playctl :
-Control file
-.br
-.BR /mnt/playlist :
-Playlist file
-.br
-.BR /mnt/playvol :
-Volume control file
-.SH SOURCE
-.B /sys/src/games/music/playlistfs
-.SH SEE ALSO
-.IR play (1),
-.IR audio (1),
-.IR juke (7).
--- a/sys/src/games/mkfile	Tue Apr 13 07:20:27 2021
+++ b/sys/src/games/mkfile	Tue Apr 13 07:25:24 2021
@@ -40,7 +40,6 @@
 	mahjongg\
 	mines\
 	mix\
-	music\
 	md\
 	nes\
 	opl3\
--- a/sys/src/games/music/Readme	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,12 +0,0 @@
-The Plan 9 Jukebox consists of three applications: a play-list server, a browse server
-and a GUI:
-	playlist server: playlistfs
-	browse server: jukefs
-	GUI: jukebox
-The juke.rc script (installed, by default, in /rc/bin/juke) starts them all up.
-
-To configure, adjust the DEFAULTMAP and ICONPATH in the central mkfile.
-The default map is used by jukefs as the root of the descriptive database.
-The iconpath is used by jukebox to find the icons.
-
-Report problems to sape@plan9.bell-labs.com
--- a/sys/src/games/music/debug.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,13 +0,0 @@
-extern int debug;
-
-enum {
-	DBGSERVER	= 0x01,
-	DBGCONTROL	= 0x02,
-	DBGCTLGRP	= 0x04,
-	DBGPICKLE	= 0x08,
-	DBGSTATE	= 0x10,
-	DBGPLAY		= 0x20,
-	DBGPUMP		= 0x40,
-	DBGTHREAD	= 0x80,
-	DBGFILEDUMP	= 0x100,
-};
Binary files a/sys/src/games/music/icon/next.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/pause.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/play.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/prev.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/question.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/root.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/skull.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/stop.bit and /dev/null differ
Binary files a/sys/src/games/music/icon/trash.bit and /dev/null differ
--- a/sys/src/games/music/juke.rc	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,57 +0,0 @@
-#!/bin/rc
-rfork e
-wide=`{echo $vgasize | sed 's/(.*)x.*x.*/\1 > 240/' | hoc}
-debug=0
-tflag=''
-wflag=''
-host=''
-flags=()
-sname=$user
-if (! ~ $wide 1) {
-	flags=($flags -t)
-}
-while(! ~ $#* 0) {
-	switch ($1) {
-	case -d
-		debug=$2
-		shift
-	case -t
-		tflag='-t'
-	case -h
-		host=$2
-		shift
-	case -w
-		wflags='-w'
-	case -s
-		sname=$2
-		shift
-	case -*
-		echo Usage: classical [-d level] [-t] [-h srvhost]
-		exit usage
-	}
-	shift
-}
-if (! test -f /mnt/playlist) {
-	if (! ~ $debug '0') echo mounting playlistfs
-	if (! test -e /srv/playlist.$sname && ! ~ $host ''){
-		import -a $host /srv /srv
-	}
-	if (! mount -b /srv/playlist.$sname /mnt >/dev/null >[2]/dev/null){
-		rm -f /srv/playlist.$sname
-		if (! ~ $debug '0') echo starting playlistfs
-		games/playlistfs -s $sname -d $debug
-	}
-}
-if (~ `{ls /mnt/juke >[2]/dev/null | sed '1q'} '') {
-	if (! test -e /srv/jukefs.$sname && ! ~ $host ''){
-		import -a $host /srv /srv
-	}
-	if (! mount -b /srv/jukefs.$sname /mnt >/dev/null >[2]/dev/null){
-		if (! ~ $debug '0') echo games/jukefs
-		games/jukefs -s $sname
-	}
-}
-if (~ $wflags '-w') {
-	exec games/jukebox -w -d $debug $tflag &
-}
-exec games/jukebox -d $debug $tflag
--- a/sys/src/games/music/jukebox/client.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,157 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <bio.h>
-#include "client.h"
-#include "playlist.h"
-#include "../debug.h"
-
-char *srvmount = "/mnt/juke";
-
-char*
-getroot(void)
-{
-	return "root";
-}
-
-void
-fillbrowsebot(char *onum)
-{
-	char *name, *p, *q;
-	Biobuf *b, *d;
-	int c;
-
-	name = smprint("%s/%s/children", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-		sysfatal("getchildren: %s: %r", name);
-	free(name);
-	while(p = Brdline(b, '\n')){
-		c = strtol(p, &q, 0);
-		assert(*q == '\n');
-		*q = 0;
-		name = smprint("%s/%d/type", srvmount, c);
-		d = Bopen(name, OREAD);
-		if(d == nil)
-			sysfatal("getchildren: %s: %r", name);
-		free(name);
-		q = Brdstr(d, '\n', 1);
-		if(q == nil){
-			abort();
-		}
-		Bterm(d);
-		if(strcmp(q, "performance") == 0)
-			continue;
-		name = smprint("%s/%d/digest", srvmount, c);
-		d = Bopen(name, OREAD);
-		if(d == nil)
-			sysfatal("getchildren: %s: %r", name);
-		free(name);
-		q = Brdstr(d, '\n', 1);
-		if(q == nil){
-			Bterm(d);
-			continue;
-		}
-		addchild(strdup(p), q);
-		Bterm(d);
-	}
-	Bterm(b);
-}
-
-void
-doplay(char *onum){
-	char *name, *p, *q;
-	Biobuf *b;
-	int m;
-
-	name = smprint("%s/%s/files", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-abort();//		sysfatal("doplay: %s: %r", name);
-	while(p = Brdline(b, '\n')){
-		m = Blinelen(b);
-		assert(p[m-1] == '\n');
-		p[m-1] = '\0';
-		q = strchr(p, '	');
-		if(q == nil)
-			sysfatal("doplay: %s: format", name);
-		*q++ = '\0';
-		sendplaylist(strdup(q), strdup(p));
-	}
-	free(name);
-	Bterm(b);
-}
-
-void
-fillbrowsetop(char *onum)
-{
-	char *name, *p;
-	Biobuf *b;
-	int m;
-
-	name = smprint("%s/%s/parentage", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-abort();//		sysfatal("gettopwin: %s: %r", name);
-	free(name);
-	while(p = Brdline(b, '\n')){
-		m = Blinelen(b);
-		assert(p[m-1] == '\n');
-		p[m-1] = '\0';
-		addparent(p);
-	}
-	Bterm(b);
-}
-
-void
-fillplaytext(char *onum)
-{
-	char *name, *p;
-	Biobuf *b;
-	int m;
-
-	name = smprint("%s/%s/parentage", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-		sysfatal("fillplaytext: %s: %r", name);
-	free(name);
-	while(p = Brdline(b, '\n')){
-		m = Blinelen(b);
-		assert(p[m-1] == '\n');
-		p[m-1] = '\0';
-		addplaytext(p);
-	}
-	Bterm(b);
-}
-
-char *
-getoneliner(char *onum)
-{
-	char *name, *p;
-	Biobuf *b;
-
-	name = smprint("%s/%s/miniparentage", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-		sysfatal("gettopwin: %s: %r", name);
-	free(name);
-	p = Brdstr(b, '\n', 1);
-	Bterm(b);
-	return p;
-}
-
-char *
-getparent(char *onum)
-{
-	char *name, *p;
-	Biobuf *b;
-
-	name = smprint("%s/%s/parent", srvmount, onum);
-	b = Bopen(name, OREAD);
-	if(b == nil)
-abort();//		sysfatal("gettopwin: %s: %r", name);
-	free(name);
-	p = Brdstr(b, '\n', 1);
-	Bterm(b);
-	return p;
-}
--- a/sys/src/games/music/jukebox/client.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,10 +0,0 @@
-char*	getroot(void);
-void	doplay(char*);
-void	fillbrowsebot(char*);
-void	fillbrowsetop(char*);
-void	fillplaytext(char*);
-void	addchild(char*, char*);
-void	addparent(char*);
-char	*getoneliner(char*);
-char	*getparent(char*);
-void	addplaytext(char*);
--- a/sys/src/games/music/jukebox/colors.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,120 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <draw.h>
-#include <keyboard.h>
-#include <mouse.h>
-#include <control.h>
-#include "colors.h"
-
-Font *boldfont;
-Font *romanfont;
-
-Image		*background;
-Image		*bordercolor;
-Image		*black;
-Image		*blue;
-Image		*darkblue;
-Image		*darkgrey;
-Image		*darkgreen;
-Image		*darkmagenta;
-Image		*green;
-Image		*grey;
-Image		*high;
-Image		*land;
-Image		*lightblue;
-Image		*lightgreen;
-Image		*lightgrey;
-Image		*lightmagenta;
-Image		*low;
-Image		*magenta;
-Image		*oceanblue;
-Image		*pale;
-Image		*paleblue;
-Image		*paleyellow;
-Image		*red;
-Image		*sea;
-Image		*white;
-Image		*yellow;
-
-static ulong
-rgba(ulong rgba)
-{
-	uchar r, g, b, a;
-
-	a = rgba & 0xff;
-	b = (rgba >>= 8) & 0xff;
-	g = (rgba >>= 8) & 0xff;
-	r = (rgba >> 8) & 0xff;
-	rgba = ((r * a / 0xff) & 0xff);
-	rgba = ((g * a / 0xff) & 0xff) | (rgba << 8);
-	rgba = ((b * a / 0xff) & 0xff) | (rgba << 8);
-	rgba = (a & 0xff) | (rgba << 8);
-	return rgba;
-}
-
-void
-colorinit(char *roman, char *bold)
-{
-	Rectangle r = Rect(0, 0, 1, 1);
-
-	white =			display->white;
-	black =			display->black;
-	blue =			allocimage(display, r, screen->chan, 1, rgba(0x0000ffff));
-	darkblue =		allocimage(display, r, screen->chan, 1, rgba(0x0000ccff));
-	darkgrey =		allocimage(display, r, screen->chan, 1, rgba(0x444444ff));
-	darkgreen =		allocimage(display, r, screen->chan, 1, rgba(0x008800ff));
-	darkmagenta =		allocimage(display, r, screen->chan, 1, rgba(0x770077ff));
-	green =			allocimage(display, r, screen->chan, 1, rgba(0x00ff00ff));
-	grey =			allocimage(display, r, screen->chan, 1, rgba(0x888888ff));
-	high =			allocimage(display, r, screen->chan, 1, rgba(0x00ccccff));
-	land =			allocimage(display, r, screen->chan, 1, rgba(0xe0ffe0ff));
-	lightblue =		allocimage(display, r, screen->chan, 1, rgba(0x88ccccff));
-	lightgreen =		allocimage(display, r, screen->chan, 1, rgba(0xaaffaaff));
-	lightgrey =		allocimage(display, r, screen->chan, 1, rgba(0xddddddff));
-	lightmagenta =		allocimage(display, r, screen->chan, 1, rgba(0xff88ffff));
-	low =			allocimage(display, r, screen->chan, 1, rgba(0xddddddff));
-	magenta =		allocimage(display, r, screen->chan, 1, rgba(0xbb00bbff));
-	oceanblue =		allocimage(display, r, screen->chan, 1, rgba(0x93ddddff));
-	pale =			allocimage(display, r, screen->chan, 1, rgba(0xffffaaff));
-	paleblue =		allocimage(display, r, screen->chan, 1, rgba(0xddffffff));
-	paleyellow =		allocimage(display, r, screen->chan, 1, rgba(0xeeee9eff));
-	red =			allocimage(display, r, screen->chan, 1, rgba(0xff0000ff));
-	sea =			allocimage(display, r, screen->chan, 1, rgba(0xe0e0ffff));
-	yellow =			allocimage(display, r, screen->chan, 1, rgba(0xffff00ff));
-	background = sea;
-	bordercolor = darkgreen;
-
-	namectlimage(background, "background");
-	namectlimage(bordercolor, "border");
-	namectlimage(black, "black");
-	namectlimage(blue, "blue");
-	namectlimage(darkblue, "darkblue");
-	namectlimage(darkgreen, "darkgreen");
-	namectlimage(darkmagenta, "darkmagenta");
-	namectlimage(green, "green");
-	namectlimage(grey, "grey");
-	namectlimage(high, "high");
-	namectlimage(land, "land");
-	namectlimage(lightblue, "lightblue");
-	namectlimage(lightgreen, "lightgreen");
-	namectlimage(lightgrey, "lightgrey");
-	namectlimage(lightmagenta, "lightmagenta");
-	namectlimage(low, "low");
-	namectlimage(magenta, "magenta");
-	namectlimage(oceanblue, "oceanblue");
-	namectlimage(pale, "pale");
-	namectlimage(paleblue, "paleblue");
-	namectlimage(paleyellow, "paleyellow");
-	namectlimage(red, "red");
-	namectlimage(sea, "sea");
-	namectlimage(white, "white");
-	namectlimage(yellow, "yellow");
-
-	if ((romanfont = openfont(display, roman)) == nil)
-		sysfatal("openfont %s: %r", roman);
-	namectlfont(romanfont, "romanfont");
-	if ((boldfont = openfont(display, bold)) == nil)
-		sysfatal("openfont %s: %r", bold);
-	namectlfont(boldfont, "boldfont");
-}
--- a/sys/src/games/music/jukebox/colors.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,30 +0,0 @@
-extern	Image		*background;
-extern	Image		*bordercolor;
-extern	Image		*black;
-extern	Image		*blue;
-extern	Image		*darkblue;
-extern	Image		*darkgrey;
-extern	Image		*darkgreen;
-extern	Image		*darkmagenta;
-extern	Image		*green;
-extern	Image		*grey;
-extern	Image		*high;
-extern	Image		*land;
-extern	Image		*lightblue;
-extern	Image		*lightgreen;
-extern	Image		*lightgrey;
-extern	Image		*lightmagenta;
-extern	Image		*low;
-extern	Image		*magenta;
-extern	Image		*oceanblue;
-extern	Image		*pale;
-extern	Image		*paleblue;
-extern	Image		*paleyellow;
-extern	Image		*red;
-extern	Image		*sea;
-extern	Image		*white;
-extern	Image		*yellow;
-
-extern	Font			*romanfont, *boldfont;
-
-void	colorinit(char*, char*);
--- a/sys/src/games/music/jukebox/mk.dep	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,4 +0,0 @@
-client.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/bio.h client.h playlist.h
-colors.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/draw.h /sys/include/keyboard.h /sys/include/mouse.h /sys/include/control.h colors.h
-music.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/draw.h /sys/include/keyboard.h /sys/include/mouse.h /sys/include/control.h colors.h client.h playlist.h
-playlist.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/draw.h /sys/include/keyboard.h /sys/include/mouse.h /sys/include/control.h playlist.h
--- a/sys/src/games/music/jukebox/mkfile	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,16 +0,0 @@
-</$objtype/mkfile
-<../mkinc
-
-CFLAGS = -DDEFAULTMAP="$DEFAULTMAP" -DICONPATH="$ICONPATH"
-TARG = jukebox
-BIN = /$objtype/bin/games
-
-CFILES=\
-	client.c\
-	colors.c\
-	music.c\
-	playlist.c\
-
-OFILES = ${CFILES:%.c=%.$O}
-
-</sys/src/cmd/mkone
--- a/sys/src/games/music/jukebox/music.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,1091 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <draw.h>
-#include <keyboard.h>
-#include <mouse.h>
-#include <control.h>
-#include "colors.h"
-#include "client.h"
-#include "playlist.h"
-#include "../debug.h"
-
-int	debug = 0; //DBGSERVER|DBGPUMP|DBGSTATE|DBGPICKLE|DBGPLAY;
-
-char	usage[] = "Usage: %s [-d mask] [-t] [-w]\n";
-
-typedef struct But {
-	char	*name;
-	Control	*ctl;
-} But;
-
-typedef struct Simpleitem {
-	char	*address;
-	char	*data;
-} Simpleitem;
-
-typedef struct Multiitem {
-	char	*address;
-	int	ndata;
-	char	**data;
-} Multiitem;
-
-enum {
-	WinBrowse,
-	WinPlay,
-	WinPlaylist,
-	WinError,
-	Topselect = 0x7fffffff,
-
-	Browsedepth = 63,
-};
-
-typedef enum {
-	PlayIdle,
-	PlayStart,
-	Playing,
-	PlayPause,
-} Playstate;
-
-typedef enum {
-	User,
-	Troot,
-	Rroot,
-	Tchildren,
-	Rchildren,
-	Tparent,
-	Rparent,
-	Tinfo,
-	Rinfo,
-	Tparentage,
-	Rparentage,
-	Tplay,
-	Rplay,
-} Srvstate;
-
-enum {
-	Exitbutton,
-	Pausebutton,
-	Playbutton,
-	Stopbutton,
-	Prevbutton,
-	Nextbutton,
-	Rootbutton,
-	Deletebutton,
-	Helpbutton,
-	Volume,
-	Browsetopwin,
-	Browsebotwin,
-	Browsebotscr,
-	Playevent,
-	Playlistwin,
-	Nalt,
-};
-
-But buts[] = {
-	[Exitbutton] =		{"skull", nil},
-	[Pausebutton] =		{"pause", nil},
-	[Playbutton] =		{"play", nil},
-	[Stopbutton] =		{"stop", nil},
-	[Prevbutton] =		{"prev", nil},
-	[Nextbutton] =		{"next", nil},
-	[Rootbutton] =		{"root", nil},
-	[Deletebutton] =	{"trash", nil},
-	[Helpbutton] =		{"question", nil},
-};
-
-struct tab {
-	char *tabname;
-	char *winname;
-	Control *tab;
-	Control *win;
-} tabs[4] = {
-	[WinBrowse] =	{"Browse",	"browsewin",	nil, nil},
-	[WinPlay] =	{"Playing",	"playwin",	nil, nil},
-	[WinPlaylist] =	{"Playlist",	"listwin",	nil, nil},
-	[WinError] =	{"Errors",	"errorwin",	nil, nil},
-};
-
-char *helptext[] = {
-	"Buttons, left to right:",
-	"    Exit: exit jukebox",
-	"    Pause: pause/resume playback",
-	"    Play: play selection in Playlist",
-	"    Stop: stop playback",
-	"    Prev: play previous item in Playlist",
-	"    Next: play next item in Playlist",
-	"    Root: browse to root of database tree",
-	"    Delete: empty Playlist, reread database",
-	"    Help: show this window",
-	"",
-	"Browse window: (click tab to bring forward)",
-	"  Top window displays current item",
-	"  Bottom window displays selectable subitems",
-	"  Mouse commands:",
-	"    Left: selected subitem becomes current",
-	"    Right: parent of current item becomes current",
-	"    Middle: add item or subitem to Playlist",
-	"",
-	"Playing window",
-	"  Displays item currently playing",
-	"",
-	"Playlist window",
-	"  Displays contents of Playlist",
-	"  Mouse commands:",
-	"    Left: select item",
-	"    (then click the play button)",
-	"",
-	"Error window",
-	"  Displays error messages received from player",
-	"  (e.g., can't open file)",
-	nil,
-};
-
-struct Browsestack {
-	char	*onum;
-	int	scrollpos;
-} browsestack[Browsedepth];
-int browsesp;	/* browse stack pointer */
-int browseline;	/* current browse position */
-
-Control		*vol;
-Control		*browsetopwin;
-Control		*browsebotwin;
-Control		*playlistwin;
-Control		*errortext;
-Control		*browsetopscr;
-Control		*browsebotscr;
-
-Playstate	playstate;
-
-ulong		playingbuts = 1<<Pausebutton | 1<<Stopbutton | 1<<Prevbutton | 1<<Nextbutton;
-ulong		activebuts;
-
-int		tabht;
-Image		*vol1img;
-Image		*vol2img;
-
-int		resizeready;
-int		borderwidth = 1;
-int		butht, butwid;
-int		errorlines;
-
-int		tflag;
-int		pflag;
-
-Controlset	*cs;
-
-char		*root;
-Multiitem	parent;
-Simpleitem	children[2048];
-int		nchildren;
-
-int		selected;
-
-Channel		*playevent;
-
-void
-readbuts(void)
-{
-	static char str[32], file[64];
-	But *b;
-	int fd;
-	Image *img, *mask;
-
-	for(b = buts; b < &buts[nelem(buts)]; b++){
-		sprint(file, "%s/%s.bit", ICONPATH, b->name);
-		if((fd = open(file, OREAD)) < 0)
-			sysfatal("open: %s: %r", file);
-		mask = readimage(display, fd, 0);
-		close(fd);
-		butwid = Dx(mask->r);
-		butht = Dy(mask->r);
-		b->ctl = createbutton(cs, b->name);
-		chanprint(cs->ctl, "%q align center", b->name);
-		chanprint(cs->ctl, "%q border 0", b->name);
-
-		img = allocimage(display, mask->r, screen->chan, 0, 0xe0e0ffff);
-		draw(img, img->r, darkgreen, mask, mask->r.min);
-		sprint(str, "%s.active", b->name);
-		namectlimage(img, str);
-
-		img = allocimage(display, mask->r, screen->chan, 0, 0xe0e0ffff);
-		draw(img, img->r, lightblue, mask, mask->r.min);
-		sprint(str, "%s.passive", b->name);
-		namectlimage(img, str);
-
-		chanprint(cs->ctl, "%q image %q", b->name, str);
-		sprint(str, "%s.mask", b->name);
-		namectlimage(mask, str);
-		chanprint(cs->ctl, "%q mask %q", b->name, str);
-		chanprint(cs->ctl, "%q light red", b->name);
-		chanprint(cs->ctl, "%q size %d %d %d %d", b->name, butwid, butht, butwid, butht);
-	}
-}
-
-void
-activatebuttons(ulong mask)
-{	// mask bit i corresponds to buts[i];
-	ulong bit;
-	But *b;
-	static char str[40];
-	int i;
-
-	for(i = 0; i < nelem(buts); i++){
-		b = &buts[i];
-		bit = 1 << i;
-		if((mask & bit) && (activebuts & bit) == 0){
-			// button was `deactive'
-			activate(b->ctl);
-			activebuts |= bit;
-			sprint(str, "%s.active", b->name);
-			chanprint(cs->ctl, "%q image %q", b->name, str);
-			chanprint(cs->ctl, "%q show", b->name);
-		}
-	}
-}
-
-void
-deactivatebuttons(ulong mask)
-{	// mask bit i corresponds with buts[i];
-	ulong bit;
-	But *b;
-	static char str[40];
-	int i;
-
-	for(i = 0; i < nelem(buts); i++){
-		b = &buts[i];
-		bit = 1 << i;
-		if((mask & bit) && (activebuts & bit)){
-			// button was active
-			deactivate(b->ctl);
-			activebuts &= ~bit;
-			sprint(str, "%s.passive", b->name);
-			chanprint(cs->ctl, "%q image %q", b->name, str);
-			chanprint(cs->ctl, "%q show", b->name);
-		}
-	}
-}
-
-void
-resizecontrolset(Controlset *){
-	static Point pol[3];
-	char *p;
-
-	if(getwindow(display, Refbackup) < 0)
-		sysfatal("getwindow");
-	draw(screen, screen->r, bordercolor, nil, screen->r.min);
-	if(!resizeready)
-		return;
-#ifndef REPLACESEMANTICS
-	if(vol1img)
-		chanprint(cs->ctl, "volume image darkgreen");
-	if(vol2img)
-		chanprint(cs->ctl, "volume indicatorcolor red");
-	chanprint(cs->ctl, "wholewin size");
-	chanprint(cs->ctl, "wholewin rect %R", screen->r);
-
-	chanprint(cs->ctl, "sync");
-	p = recvp(cs->data);
-	if(strcmp(p, "sync"))
-		sysfatal("huh?");
-	free(p);
-
-	if(vol1img){
-		freectlimage("volume.img");
-		freeimage(vol1img);
-	}
-	if(vol2img){
-		freectlimage("indicator.img");
-		freeimage(vol2img);
-	}
-	vol1img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
-	vol2img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
-	pol[0] = Pt(vol->rect.min.x, vol->rect.max.y);
-	pol[1] = Pt(vol->rect.max.x, vol->rect.min.y);
-	pol[2] = vol->rect.max;
-	fillpoly(vol1img, pol, 3, 0, darkgreen, ZP);
-	fillpoly(vol2img, pol, 3, 0, red, ZP);
-	namectlimage(vol1img, "volume.img");
-	namectlimage(vol2img, "indicator.img");
-	chanprint(cs->ctl, "volume image volume.img");
-	chanprint(cs->ctl, "volume indicatorcolor indicator.img");
-#else
-	chanprint(cs->ctl, "wholewin size");
-	chanprint(cs->ctl, "wholewin rect %R", screen->r);
-
-	chanprint(cs->ctl, "sync");
-	p = recvp(cs->data);
-	if(strcmp(p, "sync"))
-		sysfatal("huh?");
-	free(p);
-
-	new1img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
-	new2img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
-	pol[0] = Pt(vol->rect.min.x, vol->rect.max.y);
-	pol[1] = Pt(vol->rect.max.x, vol->rect.min.y);
-	pol[2] = vol->rect.max;
-	fillpoly(new1img, pol, 3, 0, darkgreen, ZP);
-	fillpoly(new2img, pol, 3, 0, red, ZP);
-	namectlimage(new1img, "volume.img");
-	namectlimage(new2img, "indicator.img");
-	if(vol1img)
-		freeimage(vol1img);
-	else
-		chanprint(cs->ctl, "volume image volume.img");
-	if(vol2img)
-		freeimage(vol2img);
-	else
-		chanprint(cs->ctl, "volume indicatorcolor indicator.img");
-	vol1img = new1img;
-	vol2img = new2img;
-#endif
-	chanprint(cs->ctl, "browsetopscr vis '%d'",
-		Dy(controlcalled("browsetopscr")->rect)/romanfont->height);
-	chanprint(cs->ctl, "browsebotscr vis '%d'",
-		Dy(controlcalled("browsebotscr")->rect)/romanfont->height);
-	chanprint(cs->ctl, "playscr vis '%d'",
-		Dy(controlcalled("playscr")->rect)/romanfont->height);
-	chanprint(cs->ctl, "playlistscr vis '%d'",
-		Dy(controlcalled("playlistscr")->rect)/romanfont->height);
-	chanprint(cs->ctl, "wholewin show");
-}
-
-void
-maketab(void)
-{
-	int i;
-
-	tabht = boldfont->height + 1 + borderwidth;
-
-	createtab(cs, "tabs");
-
-	for(i = 0; i < nelem(tabs); i++){
-		tabs[i].tab = createtextbutton(cs, tabs[i].tabname);
-		chanprint(cs->ctl, "%q size %d %d %d %d", tabs[i].tab->name,
-			stringwidth(boldfont, tabs[i].tabname), tabht, 1024, tabht);
-		chanprint(cs->ctl, "%q align uppercenter", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q font boldfont", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q image background", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q light background", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q pressedtextcolor red", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q textcolor darkgreen", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q mask transparent", tabs[i].tab->name);
-		chanprint(cs->ctl, "%q text %q", tabs[i].tab->name, tabs[i].tabname);
-
-		chanprint(cs->ctl, "tabs add %s %s", tabs[i].tabname, tabs[i].winname);
-	}
-
-	chanprint(cs->ctl, "tabs separation %d", 2);
-	chanprint(cs->ctl, "tabs image background");
-	chanprint(cs->ctl, "tabs value 0");
-}
-
-void
-makeplaycontrols(void)
-{
-	int w;
-	Control *playscr;
-
-	w = stringwidth(romanfont, "Roll over Beethoven");
-	playscr = createslider(cs, "playscr");
-	chanprint(cs->ctl, "playscr size 12, 24, 12, 1024");
-	createtext(cs, "playtext");
-	chanprint(cs->ctl, "playtext size %d %d %d %d",
-		w, 5*romanfont->height, 2048, 1024);
-
-	chanprint(cs->ctl, "playscr format '%%s: playtext topline %%d'");
-	controlwire(playscr, "event", cs->ctl);
-
-	tabs[WinPlay].win = createrow(cs, tabs[WinPlay].winname);
-	chanprint(cs->ctl, "%q add playscr playtext", tabs[WinPlay].win->name);
-}
-
-void
-makebrowsecontrols(void)
-{
-	int w;
-
-	w = stringwidth(romanfont, "Roll over Beethoven");
-	browsetopscr = createslider(cs, "browsetopscr");
-	chanprint(cs->ctl, "browsetopscr size 12, 24, 12, %d", 12*romanfont->height);
-	browsetopwin = createtext(cs, "browsetopwin");
-	chanprint(cs->ctl, "browsetopwin size %d %d %d %d",
-		w, 3*romanfont->height, 2048, 12*romanfont->height);
-	createrow(cs, "browsetop");
-	chanprint(cs->ctl, "browsetop add browsetopscr browsetopwin");
-
-	browsebotscr = createslider(cs, "browsebotscr");
-	chanprint(cs->ctl, "browsebotscr size 12, 24, 12, 1024");
-	browsebotwin = createtext(cs, "browsebotwin");
-	chanprint(cs->ctl, "browsebotwin size %d %d %d %d",
-		w, 3*romanfont->height, 2048, 1024);
-	createrow(cs, "browsebot");
-	chanprint(cs->ctl, "browsebot add browsebotscr browsebotwin");
-
-	chanprint(cs->ctl, "browsetopscr format '%%s: browsetopwin topline %%d'");
-	controlwire(browsetopscr, "event", cs->ctl);
-//	chanprint(cs->ctl, "browsebotscr format '%%s: browsebotwin topline %%d'");
-//	controlwire(browsebotscr, "event", cs->ctl);
-
-	tabs[WinBrowse].win = createcolumn(cs, tabs[WinBrowse].winname);
-	chanprint(cs->ctl, "%q add browsetop browsebot", tabs[WinBrowse].win->name);
-}
-
-void
-makeplaylistcontrols(void)
-{
-	int w;
-	Control *playlistscr;
-
-	w = stringwidth(romanfont, "Roll over Beethoven");
-	playlistscr = createslider(cs, "playlistscr");
-	chanprint(cs->ctl, "playlistscr size 12, 24, 12, 1024");
-	playlistwin = createtext(cs, "playlistwin");
-	chanprint(cs->ctl, "playlistwin size %d %d %d %d",
-		w, 5*romanfont->height, 2048, 1024);
-//	chanprint(cs->ctl, "playlistwin selectmode multi");
-
-	chanprint(cs->ctl, "playlistscr format '%%s: playlistwin topline %%d'");
-	controlwire(playlistscr, "event", cs->ctl);
-
-	tabs[WinPlaylist].win = createrow(cs, tabs[WinPlaylist].winname);
-	chanprint(cs->ctl, "%q add playlistscr playlistwin", tabs[WinPlaylist].win->name);
-}
-
-void
-makeerrorcontrols(void)
-{
-	int w;
-	Control *errorscr;
-
-	w = stringwidth(romanfont, "Roll over Beethoven");
-	errorscr = createslider(cs, "errorscr");
-	chanprint(cs->ctl, "errorscr size 12, 24, 12, 1024");
-	errortext = createtext(cs, "errortext");
-	chanprint(cs->ctl, "errortext size %d %d %d %d",
-		w, 5*romanfont->height, 2048, 1024);
-	chanprint(cs->ctl, "errortext selectmode multi");
-
-	chanprint(cs->ctl, "errorscr format '%%s: errortext topline %%d'");
-	controlwire(errorscr, "event", cs->ctl);
-
-	tabs[WinError].win = createrow(cs, tabs[WinError].winname);
-	chanprint(cs->ctl, "%q add errorscr errortext", tabs[WinError].win->name);
-}
-
-void
-makecontrols(void)
-{
-	int i;
-
-	cs = newcontrolset(screen, nil, nil, nil);
-
-	// make shared buttons
-	readbuts();
-
-	vol = createslider(cs, "volume");
-	chanprint(cs->ctl, "volume size %d %d %d %d", 2*butwid, butht, 2048, butht);
-	chanprint(cs->ctl, "volume absolute 1");
-	chanprint(cs->ctl, "volume indicatorcolor red");
-	chanprint(cs->ctl, "volume max 100");
-	chanprint(cs->ctl, "volume orient hor");
-	chanprint(cs->ctl, "volume clamp low 1");
-	chanprint(cs->ctl, "volume clamp high 0");
-	chanprint(cs->ctl, "volume format '%%s volume %%d'");
-
-	createrow(cs, "buttonrow");
-	for(i = 0; i < nelem(buts); i++)
-		chanprint(cs->ctl, "buttonrow add %s", buts[i].name);
-	chanprint(cs->ctl, "buttonrow add volume");
-	chanprint(cs->ctl, "buttonrow separation %d", borderwidth);
-	chanprint(cs->ctl, "buttonrow image darkgreen");
-
-	makebrowsecontrols();
-	makeplaycontrols();
-	makeplaylistcontrols();
-	makeerrorcontrols();
-
-	maketab();
-
-	chanprint(cs->ctl, "%q image background", "text slider");
-	chanprint(cs->ctl, "text font romanfont");
-	chanprint(cs->ctl, "slider indicatorcolor darkgreen");
-	chanprint(cs->ctl, "row separation %d", borderwidth);
-	chanprint(cs->ctl, "row image darkgreen");
-	chanprint(cs->ctl, "column separation %d", 2);
-	chanprint(cs->ctl, "column image darkgreen");
-
-	createcolumn(cs, "wholewin");
-	chanprint(cs->ctl, "wholewin separation %d", borderwidth);
-	chanprint(cs->ctl, "wholewin add buttonrow tabs");
-	chanprint(cs->ctl, "wholewin image darkgreen");
-	chanprint(cs->ctl, "%q image darkgreen", "column row");
-}
-
-void
-makewindow(int dx, int dy, int wflag){
-	int mountfd, fd, n;
-	static char aname[128];
-	static char rio[128] = "/mnt/term";
-	char *args[6];
-
-	if(wflag){
-		/* find out screen size */
-		fd = open("/mnt/wsys/screen", OREAD);
-		if(fd >= 0 && read(fd, aname, 60) == 60){
-			aname[60] = '\0';
-			n = tokenize(aname, args, nelem(args));
-			if(n != 5)
-				fprint(2, "Not an image: /mnt/wsys/screen\n");
-			else{
-				n = atoi(args[3]) - atoi(args[1]);
-				if(n <= 0 || n > 2048)
-					fprint(2, "/mnt/wsys/screen very wide: %d\n", n);
-				else
-					if(n < dx) dx = n-1;
-				n = atoi(args[4]) - atoi(args[2]);
-				if(n <= 0 || n > 2048)
-					fprint(2, "/mnt/wsys/screen very high: %d\n", n);
-				else
-					if(n < dy) dy = n-1;
-			}
-			close(fd);
-		}
-		n = 0;
-		if((fd = open("/env/wsys", OREAD)) < 0){
-			n = strlen(rio);
-			fd = open("/mnt/term/env/wsys", OREAD);
-			if(fd < 0)
-				sysfatal("/env/wsys");
-		}
-		if(read(fd, rio+n, sizeof(rio)-n-1) <= 0)
-			sysfatal("/env/wsys");
-		mountfd = open(rio, ORDWR);
-		if(mountfd < 0)
-			sysfatal("open %s: %r", rio);
-		snprint(aname, sizeof aname, "new -dx %d -dy %d", dx, dy);
-		rfork(RFNAMEG);
-		if(mount(mountfd, -1, "/mnt/wsys", MREPL, aname) < 0)
-			sysfatal("mount: %r");
-		if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
-			sysfatal("mount: %r");
-	}
-
-	if(initdraw(nil, nil, "music") < 0)
-		sysfatal("initdraw: %r");
-
-	initcontrols();
-	if(dx <= 320)
-		colorinit("/lib/font/bit/lucidasans/unicode.6.font",
-			"/lib/font/bit/lucidasans/boldunicode.8.font");
-	else
-		colorinit("/lib/font/bit/lucidasans/unicode.8.font",
-			"/lib/font/bit/lucidasans/boldunicode.10.font");
-	makecontrols();
-	resizeready = 1;
-
-	resizecontrolset(cs);
-	if(debug & DBGCONTROL)
-		fprint(2, "resize done\n");
-}
-
-void
-setparent(char *addr)
-{
-	int i;
-
-	if(parent.address)
-		free(parent.address);
-	parent.address = strdup(addr);
-	for(i = 0; i < parent.ndata; i++)
-		if(parent.data[i])
-			free(parent.data[i]);
-	parent.ndata = 0;
-	if(parent.data){
-		free(parent.data);
-		parent.data = nil;
-	}
-	chanprint(cs->ctl, "browsetopwin clear");
-	chanprint(cs->ctl, "browsetopscr max 0");
-	chanprint(cs->ctl, "browsetopscr value 0");
-}
-
-void
-addparent(char *str)
-{
-	parent.data = realloc(parent.data, (parent.ndata+1)*sizeof(char*));
-	parent.data[parent.ndata] = strdup(str);
-	parent.ndata++;
-	chanprint(cs->ctl, "browsetopwin accumulate %q", str);
-	chanprint(cs->ctl, "browsetopscr max %d", parent.ndata);
-}
-
-void
-clearchildren(void)
-{
-	int i;
-
-	for(i = 0; i < nchildren; i++){
-		if(children[i].address)
-			free(children[i].address);
-		if(children[i].data)
-			free(children[i].data);
-	}
-	nchildren= 0;
-	chanprint(cs->ctl, "browsebotwin clear");
-	chanprint(cs->ctl, "browsebotwin topline 0");
-	chanprint(cs->ctl, "browsebotscr max 0");
-	chanprint(cs->ctl, "browsebotscr value 0");
-	selected = -1;
-}
-
-void
-addchild(char *addr, char *data)
-{
-	children[nchildren].address = addr;
-	children[nchildren].data = data;
-	nchildren++;
-	chanprint(cs->ctl, "browsebotwin accumulate %q", data);
-	chanprint(cs->ctl, "browsebotscr max %d", nchildren);
-}
-
-static void
-playlistselect(int n)
-{
-	if(playlist.selected >= 0 && playlist.selected < playlist.nentries){
-		chanprint(cs->ctl, "playlistwin select %d 0", playlist.selected);
-		deactivatebuttons(1<<Playbutton);
-	}
-	playlist.selected = n;
-	if(playlist.selected < 0 || playlist.selected >= playlist.nentries)
-		return;
-	activatebuttons(1<<Playbutton);
-	chanprint(cs->ctl, "playlistwin select %d 1", n);
-	if(n >= 0 && n <= playlist.nentries - Dy(playlistwin->rect)/romanfont->height)
-		n--;
-	else
-		n = playlist.nentries - Dy(playlistwin->rect)/romanfont->height + 1;
-	if(n < 0) n = 0;
-	if(n < playlist.nentries){
-		chanprint(cs->ctl, "playlistwin topline %d",  n);
-		chanprint(cs->ctl, "playlistscr value %d",  n);
-	}
-	chanprint(cs->ctl, "playlist show");
-}
-
-void
-updateplaylist(int trunc)
-{
-	char *s;
-	int fd;
-
-	while(cs->ctl->s - cs->ctl->n < cs->ctl->s/4)
-		sleep(100);
-	if(trunc){
-		playlistselect(-1);
-		chanprint(cs->ctl, "playlistwin clear");
-		chanprint(cs->ctl, "playlistwin topline 0");
-		chanprint(cs->ctl, "playlistscr max 0");
-		chanprint(cs->ctl, "playlistscr value 0");
-		deactivatebuttons(1<<Playbutton | 1<<Deletebutton);
-		chanprint(cs->ctl, "playlistwin show");
-		chanprint(cs->ctl, "playlistscr show");
-		s = smprint("%s/ctl", srvmount);
-		if((fd = open(s, OWRITE)) >= 0){
-			fprint(fd, "reread");
-			close(fd);
-		}
-		free(s);
-		return;
-	}
-	if(playlist.entry[playlist.nentries].onum){
-		s = getoneliner(playlist.entry[playlist.nentries].onum);
-		chanprint(cs->ctl, "playlistwin accumulate %q", s);
-		free(s);
-	}else
-		chanprint(cs->ctl, "playlistwin accumulate %q", playlist.entry[playlist.nentries].file);
-	playlist.nentries++;
-	chanprint(cs->ctl, "playlistscr max %d", playlist.nentries);
-	if(playlist.selected == playlist.nentries - 1)
-		playlistselect(playlist.selected);
-	activatebuttons(1<<Playbutton|1<<Deletebutton);
-	chanprint(cs->ctl, "playlistscr show");
-	chanprint(cs->ctl, "playlistwin show");
-}
-
-void
-browseto(char *onum, int line)
-{
-	onum = strdup(onum);
-	setparent(onum);
-	clearchildren();
-	fillbrowsetop(onum);
-	chanprint(cs->ctl, "browsetop show");
-	fillbrowsebot(onum);
-	if(line){
-		chanprint(cs->ctl, "browsebotscr value %d", line);
-		chanprint(cs->ctl, "browsebotwin topline %d", line);
-	}
-	chanprint(cs->ctl, "browsebot show");
-	free(onum);
-}
-
-void
-browsedown(char *onum)
-{
-	if(browsesp == 0){
-		/* Make room for an entry by deleting the last */
-		free(browsestack[Browsedepth-1].onum);
-		memmove(browsestack + 1, browsestack, (Browsedepth-1) * sizeof(browsestack[0]));
-		browsesp++;
-	}
-	/* Store current position in current stack frame */
-	assert(browsesp > 0 && browsesp < Browsedepth);
-	browsestack[browsesp].onum = strdup(parent.address);
-	browsestack[browsesp].scrollpos = browseline;
-	browsesp--;
-	browseline = 0;
-	if(browsestack[browsesp].onum && strcmp(browsestack[browsesp].onum, onum) == 0)
-		browseline = browsestack[browsesp].scrollpos;
-	browseto(onum, browseline);
-}
-
-void
-browseup(char *onum)
-{
-	if(browsesp == Browsedepth){
-		/* Make room for an entry by deleting the first */
-		free(browsestack[0].onum);
-		memmove(browsestack, browsestack + 1, browsesp * sizeof(browsestack[0]));
-		browsesp--;
-	}
-	/* Store current position in current stack frame */
-	assert(browsesp >= 0 && browsesp < Browsedepth);
-	browsestack[browsesp].onum = strdup(parent.address);
-	browsestack[browsesp].scrollpos = browseline;
-	browsesp++;
-	browseline = 0;
-	if(browsestack[browsesp].onum && strcmp(browsestack[browsesp].onum, onum) == 0)
-		browseline = browsestack[browsesp].scrollpos;
-	browseto(onum, browseline);
-}
-
-void
-addplaytext(char *s)
-{
-	chanprint(cs->ctl, "playtext accumulate %q", s);
-}
-
-void
-work(void)
-{
-	static char *eventstr, *args[64], *s;
-	static char buf[4096];
-	int a, n, i;
-	Alt alts[] = {
-	[Exitbutton] =		{buts[Exitbutton].ctl->event, &eventstr, CHANRCV},
-	[Pausebutton] =		{buts[Pausebutton].ctl->event, &eventstr, CHANRCV},
-	[Playbutton] =		{buts[Playbutton].ctl->event, &eventstr, CHANRCV},
-	[Stopbutton] =		{buts[Stopbutton].ctl->event, &eventstr, CHANRCV},
-	[Prevbutton] =		{buts[Prevbutton].ctl->event, &eventstr, CHANRCV},
-	[Nextbutton] =		{buts[Nextbutton].ctl->event, &eventstr, CHANRCV},
-	[Rootbutton] =		{buts[Rootbutton].ctl->event, &eventstr, CHANRCV},
-	[Deletebutton] =	{buts[Deletebutton].ctl->event, &eventstr, CHANRCV},
-	[Helpbutton] =		{buts[Helpbutton].ctl->event, &eventstr, CHANRCV},
-	[Volume] =		{vol->event, &eventstr, CHANRCV},
-	[Browsetopwin] =	{browsetopwin->event, &eventstr, CHANRCV},
-	[Browsebotwin] =	{browsebotwin->event, &eventstr, CHANRCV},
-	[Browsebotscr] =	{browsebotscr->event, &eventstr, CHANRCV},
-	[Playevent] =		{playevent, &eventstr, CHANRCV},
-	[Playlistwin] =		{playlistwin->event, &eventstr, CHANRCV},
-	[Nalt] =		{nil, nil, CHANEND}
-	};
-
-	activate(vol);
-	activate(controlcalled("tabs"));
-	activatebuttons(1 << Exitbutton | 1 << Rootbutton | 1 << Helpbutton);
-	
-	root = getroot();
-	setparent(root);
-	clearchildren();
-	addparent("Root");
-	chanprint(cs->ctl, "browsetop show");
-	fillbrowsebot(root);
-	chanprint(cs->ctl, "browsebot show");
-
-	eventstr = nil;
-	selected = -1;
-
-	for(;;){
-		a = alt(alts);
-		if(debug & DBGCONTROL)
-			fprint(2, "Event: %s\n", eventstr);
-		n = tokenize(eventstr, args, nelem(args));
-		switch(a){
-		default:
-			sysfatal("Illegal event %d in work", a);
-		case Volume:
-			if(n != 3 || strcmp(args[0], "volume") || strcmp(args[1], "volume"))
-				sysfatal("Bad Volume event[%d]: %s %s", n, args[0], args[1]);
-			setvolume(args[2]);
-			break;
-		case Exitbutton:
-			return;
-		case Pausebutton:
-			if(n != 3 || strcmp(args[0], "pause:") || strcmp(args[1], "value"))
-				sysfatal("Bad Pausebutton event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[2], "0") == 0)
-				fprint(playctlfd, "resume");
-			else
-				fprint(playctlfd, "pause");
-			break;
-		case Playbutton:
-			if(n != 3 || strcmp(args[0], "play:") || strcmp(args[1], "value"))
-				sysfatal("Bad Playbutton event[%d]: %s %s", n, args[0], args[1]);
-			if(playlist.selected >= 0){
-				fprint(playctlfd, "play %d", playlist.selected);
-			}else
-				fprint(playctlfd, "play");
-			break;
-		case Stopbutton:
-			if(n != 3 || strcmp(args[0], "stop:") || strcmp(args[1], "value"))
-				sysfatal("Bad Stopbutton event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[2], "0") == 0)
-				chanprint(cs->ctl, "%q value 1", buts[Stopbutton].ctl->name);
-			fprint(playctlfd, "stop");
-			break;
-		case Prevbutton:
-			if(n != 3 || strcmp(args[0], "prev:") || strcmp(args[1], "value"))
-				sysfatal("Bad Prevbutton event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[2], "0") == 0)
-				break;
-			chanprint(cs->ctl, "%q value 0", buts[Prevbutton].ctl->name);
-			fprint(playctlfd, "skip -1");
-			break;
-		case Nextbutton:
-			if(n != 3 || strcmp(args[0], "next:") || strcmp(args[1], "value"))
-				sysfatal("Bad Nextbutton event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[2], "0") == 0)
-				break;
-			chanprint(cs->ctl, "%q value 0", buts[Nextbutton].ctl->name);
-			fprint(playctlfd, "skip 1");
-			break;
-		case Playlistwin:
-			if(debug & (DBGCONTROL|DBGPLAY))
-				fprint(2, "Playlistevent: %s %s\n", args[0], args[1]);
-			if(n != 4 || strcmp(args[0], "playlistwin:") || strcmp(args[1], "select"))
-				sysfatal("Bad Playlistwin event[%d]: %s %s", n, args[0], args[1]);
-			n = atoi(args[2]);
-			if(n < 0 || n >= playlist.nentries)
-				sysfatal("Selecting line %d of %d", n, playlist.nentries);
-			if(playlist.selected >= 0 && playlist.selected < playlist.nentries){
-				chanprint(cs->ctl, "playlistwin select %d 0", playlist.selected);
-				chanprint(cs->ctl, "playlistwin show");
-			}
-			playlist.selected = -1;
-			deactivatebuttons(1<<Playbutton);
-			if(strcmp(args[3], "1") == 0)
-				playlistselect(n);
-			break;
-		case Rootbutton:
-			chanprint(cs->ctl, "%q value 0", buts[Rootbutton].ctl->name);
-			setparent(root);
-			clearchildren();
-			addparent("Root");
-			chanprint(cs->ctl, "browsetop show");
-			fillbrowsebot(root);
-			chanprint(cs->ctl, "browsebot show");
-			break;
-		case Deletebutton:
-			if(n != 3 || strcmp(args[0], "trash:") || strcmp(args[1], "value"))
-				sysfatal("Bad Deletebutton event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[2], "0") == 0)
-				break;
-			chanprint(cs->ctl, "%q value 0", buts[Deletebutton].ctl->name);
-			sendplaylist(nil, nil);
-			break;
-		case Helpbutton:
-			chanprint(cs->ctl, "%q value 0", buts[Helpbutton].ctl->name);
-			if(errorlines > 0){
-				chanprint(cs->ctl, "errortext clear");
-				chanprint(cs->ctl, "errortext topline 0");
-				chanprint(cs->ctl, "errorscr max 0");
-				chanprint(cs->ctl, "errorscr value 0");
-			}
-			if(errorlines >= 0){
-				for(i = 0; helptext[i]; i++)
-					chanprint(cs->ctl, "errortext accumulate %q", helptext[i]);
-				chanprint(cs->ctl, "errorscr max %d", i);
-			}
-			chanprint(cs->ctl, "errortext topline 0");
-			chanprint(cs->ctl, "errorscr value 0");
-			errorlines = -1;
-			chanprint(cs->ctl, "tabs value %d", WinError);
-			break;
-		case Browsetopwin:
-			if(n != 4 || strcmp(args[0], "browsetopwin:") || strcmp(args[1], "select"))
-				sysfatal("Bad Browsetopwin event[%d]: %s %s", n, args[0], args[1]);
-			if(strcmp(args[3], "0") == 0)
-				break;
-			chanprint(cs->ctl, "browsetopwin select %s 0", args[2]);
-			selected = -1;
-			if(strcmp(args[3], "2") == 0)
-				doplay(parent.address);
-			else if(strcmp(args[3], "4") == 0){
-				s = getparent(parent.address);
-				browsedown(s);
-			}
-			break;
-		case Browsebotwin:
-			if(n != 4 || strcmp(args[0], "browsebotwin:") || strcmp(args[1], "select"))
-				sysfatal("Bad Browsebotwin event[%d]: %s %s", n, args[0], args[1]);
-			n = atoi(args[2]);
-			if(n < 0 || n >= nchildren)
-				sysfatal("Selection out of range: %d [%d]", n, nchildren);
-			if(strcmp(args[3], "0") == 0){
-				selected = -1;
-				break;
-			}
-			if(n < 0)
-				break;
-			chanprint(cs->ctl, "browsebotwin select %d 0", n);
-			selected = n;
-			if(selected >= nchildren)
-				sysfatal("Select out of range: %d [0⋯%d)", selected, nchildren);
-			if(strcmp(args[3], "1") == 0){
-				browseup(children[selected].address);
-			}else if(strcmp(args[3], "2") == 0)
-				doplay(children[selected].address);
-			else if(strcmp(args[3], "4") == 0)
-				browsedown(getparent(parent.address));
-			break;
-		case Browsebotscr:
-			browseline = atoi(args[2]);
-			chanprint(cs->ctl, "browsebotwin topline %d", browseline);
-			break;
-		case Playevent:
-			if(n < 3 || strcmp(args[0], "playctlproc:"))
-				sysfatal("Bad Playevent event[%d]: %s", n, args[0]);
-			if(debug & (DBGCONTROL|DBGPLAY))
-				fprint(2, "Playevent: %s %s\n", args[1], args[2]);
-			if(strcmp(args[1], "error") ==0){
-				if(n != 4){
-					fprint(2, "Playevent: %s: arg count: %d\n", args[1], n);
-					break;
-				}
-				if(errorlines < 0){
-					chanprint(cs->ctl, "errortext clear");
-					chanprint(cs->ctl, "errortext topline 0");
-					chanprint(cs->ctl, "errorscr max 0");
-					chanprint(cs->ctl, "errorscr value 0");
-					errorlines = 0;
-				}
-				n = errorlines;
-				chanprint(cs->ctl, "errortext accumulate %q", args[3]);
-				chanprint(cs->ctl, "errorscr max %d", ++errorlines);
-				if(n >= 0 && n <= errorlines - Dy(errortext->rect)/romanfont->height)
-					n--;
-				else
-					n = errorlines - Dy(errortext->rect)/romanfont->height + 1;
-				if(n < 0) n = 0;
-				if(n < errorlines){
-					chanprint(cs->ctl, "errortext topline %d",  n);
-					chanprint(cs->ctl, "errorscr value %d",  n);
-				}
-				chanprint(cs->ctl, "tabs value %d", WinError);
-			}else if(strcmp(args[1], "play") ==0){
-				chanprint(cs->ctl, "%q value 1", buts[Playbutton].ctl->name);
-				chanprint(cs->ctl, "%q value 0", buts[Stopbutton].ctl->name);
-				chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
-				playlistselect(strtoul(args[2], nil, 0));
-				chanprint(cs->ctl, "playtext clear");
-				chanprint(cs->ctl, "playtext topline 0");
-				chanprint(cs->ctl, "playscr max 0");
-				chanprint(cs->ctl, "playscr value 0");
-				playstate = Playing;
-				activatebuttons(playingbuts);
-				qlock(&playlist);
-				if(playlist.selected < playlist.nentries){
-					fillplaytext(playlist.entry[playlist.selected].onum);
-					chanprint(cs->ctl, "playscr max %d", n);
-				}
-				qunlock(&playlist);
-				chanprint(cs->ctl, "playwin show");
-			}else if(strcmp(args[1], "stop") ==0){
-				chanprint(cs->ctl, "%q value 0", buts[Playbutton].ctl->name);
-				chanprint(cs->ctl, "%q value 1", buts[Stopbutton].ctl->name);
-				chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
-				playlistselect(strtoul(args[2], nil, 0));
-				chanprint(cs->ctl, "%q show", tabs[WinPlaylist].winname);
-				playstate = PlayIdle;
-				deactivatebuttons(playingbuts);
-			}else if(strcmp(args[1], "pause") ==0){
-				activatebuttons(playingbuts);
-				chanprint(cs->ctl, "%q value 1", buts[Playbutton].ctl->name);
-				chanprint(cs->ctl, "%q value 0", buts[Stopbutton].ctl->name);
-				if(playstate == PlayPause){
-					chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
-					playstate = Playing;
-				}else{
-					chanprint(cs->ctl, "%q value 1", buts[Pausebutton].ctl->name);
-					playstate = PlayPause;
-				}
-			}else if(strcmp(args[1], "exits") ==0){
-				threadexits("exitevent");
-			}else{
-				fprint(2, "Unknown play event:");
-				for(i=0; i<n; i++)
-					fprint(2, " %s", args[i]);
-				fprint(2, "\n");
-			}
-			break;
-		}
-		if(eventstr){
-			free(eventstr);
-			eventstr = nil;
-		}
-	}
-}
-
-void
-threadmain(int argc, char *argv[]){
-	int wflag;
-
-	wflag = 0;
-	ARGBEGIN{
-	case 'd':
-		debug = strtol(ARGF(), nil, 0);
-		break;
-	case 't':
-		tflag = 1;
-		break;
-	case 'w':
-		wflag = 1;
-		break;
-	default:
-		sysfatal(usage, argv0);
-	}ARGEND
-
-	quotefmtinstall();
-
-	if(tflag)
-		makewindow(320, 320, wflag);
-	else
-		makewindow(480, 480, wflag);
-
-	playlist.selected = -1;
-
-	playctlfd = open(playctlfile, OWRITE);
-	if(playctlfd < 0)
-		sysfatal("%s: %r", playctlfile);
-	proccreate(playlistproc, nil, 8192);
-	playevent = chancreate(sizeof(char *), 1);
-	proccreate(playctlproc, playevent, 8192);
-	proccreate(playvolproc, cs->ctl, 8192);
-
-	work();
-
-	closecontrolset(cs);
-	threadexitsall(nil);
-}
--- a/sys/src/games/music/jukebox/playlist.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,297 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <draw.h>
-#include <keyboard.h>
-#include <mouse.h>
-#include <control.h>
-#include "playlist.h"
-#include "../debug.h"
-
-char *playlistfile = "/mnt/playlist";
-char *playctlfile = "/mnt/playctl";
-char *playvolfile = "/mnt/playvol";
-char *volumefile = "/dev/audioctl";
-
-Playlist	playlist;
-int		playctlfd;
-
-void
-playlistproc(void*)
-{
-	int fd, m, n, nf;
-	static char buf[8192+1];
-	char *p, *q, *fields[4];
-
-	threadsetname("playlistproc");
-	fd = open(playlistfile, OREAD);
-	if(fd < 0)
-		sysfatal("%s: %r", playlistfile);
-	p = buf;
-	n = 0;
-	if(debug & DBGPLAY)
-		fprint(2, "playlistproc: starting\n");
-	for(;;){
-		m = read(fd, buf+n, sizeof buf - 1 - n);
-		if(m == 0){
-			if(debug & DBGPLAY)
-				fprint(2, "playlistproc: empty read\n");
-			continue;
-		}
-		if(m < 0){
-			rerrstr(buf, sizeof(buf));
-			if(strcmp(buf, "reading past eof"))
-				sysfatal("%s: %r", playlistfile);
-			for(n = 0; n < playlist.nentries; n++){
-				free(playlist.entry[n].file);
-				free(playlist.entry[n].onum);
-			}
-			if(debug & DBGPLAY)
-				fprint(2, "playlistproc: trunc\n");
-			playlist.nentries = 0;
-			free(playlist.entry);
-			playlist.entry = nil;
-			updateplaylist(1);
-			seek(fd, 0, 0);
-			p = buf;
-			n = 0;
-			continue;
-		}
-		if(debug & DBGPLAY)
-			fprint(2, "playlistproc: read %d bytes\n", m);
-		n += m;
-		p[n] = '\0';
-		while(q = strchr(p, '\n')){
-			*q = 0;
-			nf = tokenize(p, fields, nelem(fields));
-			if(nf){
-				playlist.entry = realloc(playlist.entry, (playlist.nentries+1)*sizeof playlist.entry[0]);
-				if(playlist.entry == nil)
-					sysfatal("realloc %r");
-				playlist.entry[playlist.nentries].file = strdup(fields[0]);
-				if(nf > 1){
-					playlist.entry[playlist.nentries].onum = strdup(fields[1]);
-					if(debug & DBGPLAY)
-						fprint(2, "playlistproc: [%d]: %q %q\n", playlist.nentries,
-							playlist.entry[playlist.nentries].file,
-							playlist.entry[playlist.nentries].onum);
-				}else{
-					playlist.entry[playlist.nentries].onum = nil;
-					if(debug & DBGPLAY)
-						fprint(2, "playlistproc: [%d]: %q nil\n", playlist.nentries,
-							playlist.entry[playlist.nentries].file);
-				}
-				updateplaylist(0);	// this will also update nentries
-			}
-			q++;
-			n -= q-p;
-			p = q;
-		}
-		if(n)
-			memmove(buf, p, n);
-		p = buf;
-	}
-}
-
-void
-sendplaylist(char *file, char *onum)
-{
-	static int fd = -1;
-	char *b;
-
-	if(file == nil){
-		if(fd >= 0)
-			close(fd);
-		fd = open(playlistfile, OWRITE|OTRUNC);
-		if(fd < 0)
-			sysfatal("%s: truncate: %r", playlistfile);
-		return;
-	}
-	if(fd < 0){
-		fd = open(playlistfile, OWRITE);
-		if(fd < 0)
-			sysfatal("%s: %r", playlistfile);
-	}
-	b = smprint("%q	%q\n", file, onum);
-	if(debug & DBGPLAY)
-		fprint(2, "sendplaylist @%s@\n", b);
-	if(write(fd , b, strlen(b)) != strlen(b))
-		sysfatal("sendplaylist: %r");
-}
-
-void
-playctlproc(void*a)
-{
-	int fd, n, nf;
-	static char buf[512+1];
-	char *fields[4];
-	Channel *chan;
-
-	threadsetname("playctlproc");
-	chan = a;
-	fd = open(playctlfile, OREAD);
-	if(fd < 0)
-		sysfatal("%s: %r", playctlfile);
-	for(;;){
-		n = read(fd, buf, sizeof buf -1);
-		if(n == 0)
-			continue;
-		if(n < 0)
-			sysfatal("%s: %r", playctlfile);
-		buf[n] = '\0';
-		nf = tokenize(buf, fields, nelem(fields));
-		if(nf == 0)
-			continue;
-		switch (nf){
-		default:
-			sysfatal("playctlproc: [%d]: %s", nf, fields[0]);
-		case 3:
-			chanprint(chan, "playctlproc: error %lud %q", strtoul(fields[1], nil, 0), fields[2]);
-			if(strcmp(fields[0], "error") == 0)
-				break;
-			// fall through
-		case 2:
-			chanprint(chan, "playctlproc: %s %lud", fields[0], strtoul(fields[1], nil, 0));
-		}
-	}
-}
-
-void
-sendplayctl(char *fmt, ...)
-{
-	va_list arg;
-	static int fd = -1;
-
-	va_start(arg, fmt);
-	if(debug & DBGPLAY){
-		fprint(2, "sendplayctl: fmt=%s: ", fmt);
-		fprint(2, fmt, arg);
-		fprint(2, "\n");
-	}
-	fprint(fd, fmt, arg);
-	va_end(arg);
-}
-
-void
-setvolume(char *volume)
-{
-	static int fd;
-
-	if(fd == 0){
-		fd = open(playvolfile, OWRITE);
-		if(fd < 0){
-			fprint(2, "can't open %s, (%r) opening %s instead\n", playvolfile, "/dev/volume");
-			if((fd = open("/dev/volume", OWRITE)) < 0){
-				fprint(2, "setvolume: open: %r\n");
-				return;
-			}
-		}
-	}
-	if(fd < 0)
-		return;
-	fprint(fd, "volume %s", volume);
-}
-
-void
-volumeproc(void *arg)
-{
-	int fd, n, nf, nnf, i, nlines;
-	static char buf[1024];
-	char *lines[32];
-	char *fields[8];
-	char *subfields[8];
-	Channel *ctl;
-	int volume, minvolume, maxvolume, nvolume;
-
-	ctl = arg;
-	threadsetname("volumeproc");
-	fd = open(volumefile, OREAD);
-	if(fd < 0){
-		fprint(2, "%s: %r\n", volumefile);
-		threadexits(nil);
-	}
-	for(;;){
-		n = read(fd, buf, sizeof buf -1);
-		if(n == 0)
-			continue;
-		if(n < 0){
-			fprint(2, "volumeproc: read: %r\n");
-			threadexits("volumeproc");
-		}
-		buf[n] = '\0';
-		nlines = getfields(buf, lines, nelem(lines), 1, "\n");
-		for(i = 0; i < nlines; i++){
-			nf = tokenize(lines[i], fields, nelem(fields));
-			if(nf == 0)
-				continue;
-			if(nf != 6 || strcmp(fields[0], "volume") || strcmp(fields[1], "out"))
-				continue;
-			minvolume = strtol(fields[3], nil, 0);
-			maxvolume = strtol(fields[4], nil, 0);
-			if(minvolume >= maxvolume)
-				continue;
-			nnf = tokenize(fields[2], subfields, nelem(subfields));
-			if(nnf <= 0 || nnf > 8){
-				fprint(2, "volume format error\n");
-				threadexits(nil);
-			}
-			volume = 0;
-			nvolume = 0;
-			for(i = 0; i < nnf; i++){
-				volume += strtol(subfields[i], nil, 0);
-				nvolume++;
-			}
-			volume /= nvolume;
-			volume = 100*(volume - minvolume)/(maxvolume-minvolume);
-			chanprint(ctl, "volume value %d", volume);
-		}
-	}
-}
-
-void
-playvolproc(void*a)
-{
-	int fd, n, nf, volume, nvolume, i;
-	static char buf[256+1];
-	static errors;
-	char *fields[3], *subfields[9];
-	Channel *chan;
-
-	threadsetname("playvolproc");
-	chan = a;
-	fd = open(playvolfile, OREAD);
-	if(fd < 0)
-		sysfatal("%s: %r", playvolfile);
-	for(;;){
-		n = read(fd, buf, sizeof buf -1);
-		if(n == 0)
-			continue;
-		if(n < 0){
-			fprint(2, "%s: %r\n", playvolfile);
-			threadexits("playvolproc");
-		}
-		buf[n] = '\0';
-		if(debug) fprint(2, "volumestring: %s\n", buf);
-		nf = tokenize(buf, fields, nelem(fields));
-		if(nf == 0)
-			continue;
-		if(nf != 2 || strcmp(fields[0], "volume")){
-			fprint(2, "playvolproc: [%d]: %s\n", nf, fields[0]);
-			if(errors++ > 32)
-				threadexits("playvolproc");
-			continue;
-		}
-		nvolume = tokenize(fields[1], subfields, nelem(subfields));
-		if(nvolume <= 0 || nvolume > 8){
-			fprint(2, "volume format error\n");
-			if(errors++ > 32)
-				threadexits("playvolproc");
-			continue;
-		}
-		volume = 0;
-		for(i = 0; i < nvolume; i++)
-			volume += strtol(subfields[i], nil, 0);
-		volume /= nvolume;
-		chanprint(chan, "volume value %d", volume);
-	}
-}
--- a/sys/src/games/music/jukebox/playlist.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,26 +0,0 @@
-
-typedef struct Playlistentry {
-	char	*file;
-	char	*onum;
-} Playlistentry;
-
-typedef struct Playlist {
-	QLock;
-	int		nentries;
-	int		selected;
-	Playlistentry	*entry;
-} Playlist;
-
-extern Playlist	playlist;
-extern char	*playctlfile;
-extern char	*srvmount;
-extern int	playctlfd;
-
-void	playctlproc(void*a);
-void	playlistproc(void*);
-void	playvolproc(void*a);
-void	sendplayctl(char *fmt, ...);
-void	sendplaylist(char*, char*);
-void	setvolume(char *volume);
-void	updateplaylist(int);
-void	volumeproc(void *arg);
--- a/sys/src/games/music/jukefs/catset.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,152 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-#include "object.h"
-#include "catset.h"
-
-static int debug = 0;
-
-int
-catsetneeded(int v)
-{
-	return (v / 8) + 1;
-}
-
-static void
-catsetprint(int f, Catset*cs)
-{
-	int i;
-	fprint(2, "(%p %d:", cs->bitpiece, cs->nbitpiece);
-	for (i = 0; i < cs->nbitpiece; i++)
-		fprint(f, "[%d]=%x", i, cs->bitpiece[i]);
-	fprint(2, ")");
-}
-
-void
-catsetrealloc(Catset *cs, int sz)
-{
-	if (debug) fprint(2, "catsetrealloc %p %d (%p %d)", cs, sz, cs->bitpiece, cs->nbitpiece);
-	if (sz > cs->nbitpiece) {
-		cs->bitpiece = realloc(cs->bitpiece, sz*sizeof(uchar));
-		memset(cs->bitpiece + cs->nbitpiece, 0, sz - cs->nbitpiece);
-		cs->nbitpiece = sz;
-	}
-	if (debug) fprint(2, " -> %p %d\n", cs->bitpiece, cs->nbitpiece);
-}
-
-void
-catsetfree(Catset *cs)
-{
-	free(cs->bitpiece);
-	cs->bitpiece = 0;
-	cs->nbitpiece = 0;
-}
-
-void
-catsetinit(Catset*cs, int v)
-{
-	int n;
-
-	n = catsetneeded(v);
-	if (debug) fprint(2, "catsetinit %p %d -> ", cs, v);
-	catsetrealloc(cs, n);
-	catsetset(cs, v);
-	if (debug) catsetprint(2, cs);
-	if (debug) fprint(2, "\n");
-}
-
-void
-catsetcopy(Catset*dst, Catset*src)
-{
-	if (debug) fprint(2, "catsetcopy %p %p ", dst, src);
-	if (debug) catsetprint(2, dst);
-	if (debug) fprint(2, " ");
-	if (debug) catsetprint(2, src);
-	if (dst->nbitpiece < src->nbitpiece)
-		catsetrealloc(dst, src->nbitpiece);
-	else
-		memset(dst->bitpiece, 0, dst->nbitpiece);
-	memcpy(dst->bitpiece, src->bitpiece, src->nbitpiece);
-	dst->nbitpiece = src->nbitpiece;
-	if (debug) fprint(2, "-> ");
-	if (debug) catsetprint(2, dst);
-	if (debug) fprint(2, "\n");
-}
-
-void
-catsetset(Catset*cs, int v)
-{
-	int p = v / 8;
-	int b = v % 8;
-	if (debug) fprint(2, "catsetset %p %d ", cs, v);
-	if (debug) catsetprint(2, cs);
-	cs->bitpiece[p] = 1 << b;
-	if (debug) fprint(2, "-> ");
-	if (debug) catsetprint(2, cs);
-	if (debug) fprint(2, "\n");
-}
-
-int
-catsetisset(Catset*cs)
-{
-	int i;
-
-	if (debug) fprint(2, "catsetisset %p ", cs);
-	if (debug) catsetprint(2, cs);
-	if (debug) fprint(2, "\n");
-	for (i =0; i < cs->nbitpiece; i++) {
-		if (cs->bitpiece[i])
-			return 1;
-	}
-	return 0;
-}
-
-void
-catsetorset(Catset*dst, Catset*src)
-{
-	int i;
-
-	if (debug) fprint(2, "catsetorset %p %p ", dst, src);
-	if (debug) catsetprint(2, dst);
-	if (debug) fprint(2, " ");
-	if (debug) catsetprint(2, src);
-	if (src->nbitpiece > dst->nbitpiece)
-		catsetrealloc(dst, src->nbitpiece);
-
-	for (i =0; i < src->nbitpiece; i++) {
-		dst->bitpiece[i] |= src->bitpiece[i];
-	}
-	if (debug) fprint(2, "-> ");
-	if (debug) catsetprint(2, dst);
-	if (debug) fprint(2, "\n");
-}
-
-int
-catseteq(Catset*cs1, Catset*cs2)
-{
-	int i;
-	Catset *css, * csl;
-
-	if (debug) fprint(2, "catseteq %p %p ", cs1, cs2);
-	if (debug) catsetprint(2, cs1);
-	if (debug) fprint(2, " ");
-	if (debug) catsetprint(2, cs2);
-	if (debug) fprint(2, "\n");
-	if (cs1->nbitpiece > cs2->nbitpiece) {
-		csl = cs1;
-		css = cs2;
-	} else {
-		csl = cs2;
-		css = cs1;
-	}
-	for (i =0; i < css->nbitpiece; i++) {
-		if (css->bitpiece[i] != csl->bitpiece[i])
-			return 0;
-	}
-	for (i = css->nbitpiece; i < csl->nbitpiece; i++) {
-		if (csl->bitpiece[i])
-			return 0;
-	}
-	return 1;
-}
--- a/sys/src/games/music/jukefs/catset.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,8 +0,0 @@
-void catsetrealloc(Catset*, int);
-void catsetinit(Catset*, int);
-void catsetfree(Catset*t);
-void catsetcopy(Catset*, Catset*);
-void catsetset(Catset*, int);
-int catsetisset(Catset*);
-void catsetorset(Catset*, Catset*);
-int catseteq(Catset*, Catset*);
--- a/sys/src/games/music/jukefs/fs.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,747 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <bio.h>
-#include <fcall.h>
-#include "object.h"
-
-extern int debug;
-
-extern int mfd[];
-
-enum {
-	DbgFs = 0x1000
-};
-
-typedef struct Fid Fid;
-
-enum {
-	Busy =	0x01,
-	Open =	0x02,
-	Endf =	0x04,
-};
-
-struct Fid
-{
-	QLock;
-	Qid	qid;
-	int	fid;
-	ushort	flags;
-	vlong	offset;		// offset of data[0]
-	Fid	*next;
-};
-
-Fcall	thdr;
-Fcall	rhdr;
-
-enum {
-	/* Files making up an object */
-	Qchildren,		/* Each of these must be in dirtab */
-	Qdigest,
-	Qfiles,
-	Qfulltext,
-	Qkey,
-	Qminiparentage,
-	Qparent,
-	Qparentage,
-	Qtext,
-	Qtype,
-
-	/* Other files */
-	Qtop,	/* Must follow Qtype */
-	Qclassical,
-	Qdir,
-	Qroot,
-	Qctl,
-};
-
-#define PATH(id, f)	(((id)<<8) | (f))
-#define FILE(p)		((p) & 0xff)
-#define NUM(p)		((p) >> 8)
-
-char *dirtab[] =
-{
-[Qchildren]	"children",
-[Qdigest]	"digest",
-[Qdir]		".",
-[Qfiles]	"files",
-[Qfulltext]	"fulltext",
-[Qkey]		"key",
-[Qminiparentage]"miniparentage",
-[Qparent]	"parent",
-[Qparentage]	"parentage",
-[Qtext]		"text",
-[Qtype]		"type",
-[Qtop]		nil,
-};
-
-char	*rflush(Fid*), *rauth(Fid*),
-	*rattach(Fid*), *rwalk(Fid*),
-	*ropen(Fid*), *rcreate(Fid*),
-	*rread(Fid*), *rwrite(Fid*), *rclunk(Fid*),
-	*rremove(Fid*), *rstat(Fid*), *rwstat(Fid*),
-	*rversion(Fid*);
-
-char 	*(*fcalls[])(Fid*) = {
-	[Tflush]	rflush,
-	[Tversion]	rversion,
-	[Tauth]		rauth,
-	[Tattach]	rattach,
-	[Twalk]		rwalk,
-	[Topen]		ropen,
-	[Tcreate]	rcreate,
-	[Tread]		rread,
-	[Twrite]	rwrite,
-	[Tclunk]	rclunk,
-	[Tremove]	rremove,
-	[Tstat]		rstat,
-	[Twstat]	rwstat,
-};
-
-int	messagesize = 8*1024+IOHDRSZ;
-uchar	mdata[8*1024+IOHDRSZ];
-uchar	mbuf[8*1024+IOHDRSZ];
-char	bigbuf[1<<23];	/* 8 megabytes */
-Fid	*fids;
-
-char	Eperm[] =	"permission denied";
-char	Enotdir[] =	"not a directory";
-char	Enoauth[] =	"no authentication required";
-char	Enotexist[] =	"file does not exist";
-char	Einuse[] =	"file in use";
-char	Eexist[] =	"file exists";
-char	Enotowner[] =	"not owner";
-char	Eisopen[] = 	"file already open for I/O";
-char	Excl[] = 	"exclusive use file already open";
-char	Ename[] = 	"illegal name";
-char	Ebadctl[] =	"unknown control message";
-
-Fid *newfid(int fid);
-
-static int
-lookup(char *cmd, char *list[])
-{
-	int i;
-
-	for (i = 0; list[i] != nil; i++)
-		if (strcmp(cmd, list[i]) == 0)
-			return i;
-	return -1;
-}
-
-char*
-rversion(Fid *)
-{
-	Fid *f;
-
-	if(thdr.msize < 256)
-		return "max messagesize too small";
-	if(thdr.msize < messagesize)
-		messagesize = thdr.msize;
-	rhdr.msize = messagesize;
-	if(strncmp(thdr.version, "9P2000", 6) != 0)
-		return "unknown 9P version";
-	else
-		rhdr.version = "9P2000";
-	for(f = fids; f; f = f->next)
-		if(f->flags & Busy)
-			rclunk(f);
-	return nil;
-}
-
-char*
-rauth(Fid*)
-{
-	return Enoauth;
-}
-
-char*
-rflush(Fid *)
-{
-	return 0;
-}
-
-char*
-rattach(Fid *f)
-{
-	f->flags |= Busy;
-	f->qid.type = QTDIR;
-	f->qid.vers = 0;
-	f->qid.path = PATH(0, Qtop);
-	rhdr.qid = f->qid;
-	return 0;
-}
-
-static Fid*
-doclone(Fid *f, int nfid)
-{
-	Fid *nf;
-
-	nf = newfid(nfid);
-	nf->qid = f->qid;
-	if(nf->flags & Busy)
-		return nil;
-	nf->flags |= Busy;
-	nf->flags &= ~Open;
-	return nf;
-}
-
-char*
-dowalk(Fid *f, char *name)
-{
-	int t, n, m;
-	char *rv, *p;
-
-	t = FILE(f->qid.path);	/* Type */
-
-	rv = Enotexist;
-
-	if(strcmp(name, ".") == 0 && f->qid.type == QTDIR)
-		return nil;
-	if(strcmp(name, "..") == 0){
-		switch(t){
-		case Qtop:
-		case Qclassical:
-			f->qid.path = PATH(0, Qtop);
-			f->qid.type = QTDIR;
-			f->qid.vers = 0;
-			rv = nil;
-			break;
-		case Qdir:
-		case Qroot:
-			f->qid.path = PATH(0, Qclassical);
-			f->qid.type = QTDIR;
-			f->qid.vers = 0;
-			rv = nil;
-			break;
-		}
-		return rv;
-	}
-	switch(t){
-	case Qtop:
-		/* Contains classical */
-		if(strcmp(name, "juke") == 0){
-			f->qid.path = PATH(root->tabno, Qclassical);
-			f->qid.type = QTDIR;
-			f->qid.vers = 0;
-			rv = nil;
-			break;
-		}
-		break;
-	case Qclassical:
-		/* main dir, contains `root' and object dirs */
-		if(strcmp(name, "root") == 0){
-			f->qid.path = PATH(root->tabno, Qroot);
-			f->qid.type = QTDIR;
-			f->qid.vers = 0;
-			rv = nil;
-			break;
-		}
-		if(strcmp(name, "ctl") == 0){
-			f->qid.path = PATH(root->tabno, Qctl);
-			f->qid.type = QTFILE;
-			f->qid.vers = 0;
-			rv = nil;
-			break;
-		}
-		n = strtol(name, &p, 0);
-		if(*p)
-			break;	/* Not a number */
-		if(n < 0 || n >= notab)
-			break;	/* Outside range */
-		if(otab[n] == nil)
-			break;	/* Not in object table */
-		f->qid.path = PATH(n, Qdir);
-		f->qid.type = QTDIR;
-		f->qid.vers = 0;
-		rv = nil;
-		break;
-	case Qroot:	/* Root of the object hierarchy */
-	case Qdir:	/* Object directory */
-		if((m = lookup(name, dirtab)) < 0)
-			break;
-		n = NUM(f->qid.path);
-		f->qid.path = PATH(n, m);
-		f->qid.type = QTFILE;
-		f->qid.vers = 0;
-		rv = nil;
-		break;
-	}
-	return rv;
-}
-
-char*
-rwalk(Fid *f)
-{
-	Fid *nf;
-	char *rv;
-	int i;
-
-	if(f->flags & Open)
-		return Eisopen;
-
-	rhdr.nwqid = 0;
-	nf = nil;
-
-	/* clone if requested */
-	if(thdr.newfid != thdr.fid){
-		nf = doclone(f, thdr.newfid);
-		if(nf == nil)
-			return "new fid in use";
-		f = nf;
-	}
-
-	/* if it's just a clone, return */
-	if(thdr.nwname == 0 && nf != nil)
-		return nil;
-
-	/* walk each element */
-	rv = nil;
-	for(i = 0; i < thdr.nwname; i++){
-		rv = dowalk(f, thdr.wname[i]);
-		if(rv != nil){
-			if(nf != nil)	
-				rclunk(nf);
-			break;
-		}
-		rhdr.wqid[i] = f->qid;
-	}
-	rhdr.nwqid = i;
-
-	/* we only error out if no walk  */
-	if(i > 0)
-		rv = nil;
-
-	return rv;
-}
-
-char *
-ropen(Fid *f)
-{
-	if(f->flags & Open)
-		return Eisopen;
-
-	if(thdr.mode != OREAD && FILE(f->qid.path) != Qctl)
-		return Eperm;
-	rhdr.iounit = 0;
-	rhdr.qid = f->qid;
-	f->flags |= Open;
-	f->flags &= ~Endf;
-	return nil;
-}
-
-char *
-rcreate(Fid*)
-{
-	return Eperm;
-}
-
-static long
-fileinfo(char *buf, int bufsize, int onum, int t)
-{
-	long n;
-
-	n = 0;
-	switch(t){
-	case Qchildren:
-		n = printchildren(buf, bufsize, otab[onum]);
-		break;
-	case Qdigest:
-		n = printdigest(buf, bufsize, otab[onum]);
-		break;
-	case Qfulltext:
-		n = printfulltext(buf, bufsize, otab[onum]);
-		break;
-	case Qkey:
-		n = printkey(buf, bufsize, otab[onum]);
-		break;
-	case Qparent:
-		n = printparent(buf, bufsize, otab[onum]);
-		break;
-	case Qtext:
-		n = printtext(buf, bufsize, otab[onum]);
-		break;
-	case Qtype:
-		n = printtype(buf, bufsize, otab[onum]);
-		break;
-	case Qfiles:
-		n = printfiles(buf, bufsize, otab[onum]);
-		break;
-	case Qparentage:
-		n = printparentage(buf, bufsize, otab[onum]);
-		break;
-	case Qminiparentage:
-		n = printminiparentage(buf, bufsize, otab[onum]);
-		break;
-	default:
-		sysfatal("rread: %d", t);
-	}
-	return n;
-}
-
-static void
-mkstat(Dir *d, int n, int t)
-{
-	static char buf[16];
-
-	d->uid = user;
-	d->gid = user;
-	d->muid = user;
-	d->qid.vers = 0;
-	d->qid.type = QTFILE;
-	d->type = 0;
-	d->dev = 0;
-	d->atime = time(0);
-	d->mtime = d->atime;
-	switch(t){
-	case Qtop:
-		d->name = ".";
-		d->mode = DMDIR|0555;
-		d->atime = d->mtime = time(0);
-		d->length = 0;
-		d->qid.path = PATH(0, Qtop);
-		d->qid.type = QTDIR;
-		break;
-	case Qclassical:
-		d->name = "juke";
-		d->mode = DMDIR|0555;
-		d->atime = d->mtime = time(0);
-		d->length = 0;
-		d->qid.path = PATH(0, Qclassical);
-		d->qid.type = QTDIR;
-		break;
-	case Qdir:
-		snprint(buf, sizeof buf, "%d", n);
-		d->name = buf;
-		d->mode = DMDIR|0555;
-		d->length = 0;
-		d->qid.path = PATH(n, Qdir);
-		d->qid.type = QTDIR;
-		break;
-	case Qroot:
-		d->name = "root";
-		d->mode = DMDIR|0555;
-		d->length = 0;
-		d->qid.path = PATH(0, Qroot);
-		d->qid.type = QTDIR;
-		break;
-	case Qctl:
-		d->name = "ctl";
-		d->mode = 0666;
-		d->length = 0;
-		d->qid.path = PATH(0, Qctl);
-		break;
-		d->name = "ctl";
-		d->mode = 0666;
-		d->length = 0;
-		d->qid.path = PATH(0, Qctl);
-		break;
-	case Qchildren:
-	case Qdigest:
-	case Qfiles:
-	case Qfulltext:
-	case Qkey:
-	case Qminiparentage:
-	case Qparent:
-	case Qparentage:
-	case Qtext:
-	case Qtype:
-		d->name = dirtab[t];
-		d->mode = 0444;
-		d->length = fileinfo(bigbuf, sizeof bigbuf, n, t);
-		d->qid.path = PATH(n, t);
-		break;
-	default:
-		sysfatal("mkstat: %d", t);
-	}
-}
-
-int
-readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
-{
-	int m, n;
-	Dir d;
-
-	n = 0;
-	mkstat(&d, 0, Qclassical);
-	m = convD2M(&d, &buf[n], blen);
-	if(off <= 0){
-		if(m <= BIT16SZ || m > cnt)
-			return n;
-		n += m;
-	}
-	return n;
-}
-
-int
-readclasdir(Fid*, uchar *buf, long off, int cnt, int blen)
-{
-	int m, n;
-	long pos;
-	Dir d;
-	Fid *fid;
-
-	n = 0;
-	pos = 0;
-	mkstat(&d, 0, Qctl);
-	m = convD2M(&d, &buf[n], blen);
-	if(off <= pos){
-		if(m <= BIT16SZ || m > cnt)
-			return 0;
-		n += m;
-		cnt -= m;
-	}
-	pos += m;
-	mkstat(&d, 0, Qroot);
-	m = convD2M(&d, &buf[n], blen);
-	if(off <= pos){
-		if(m <= BIT16SZ || m > cnt)
-			return n;
-		n += m;
-		cnt -= m;
-	}
-	pos += m;
-	for (fid = fids; fid; fid = fid->next){
-		if(FILE(fid->qid.path) != Qdir)
-			continue;
-		mkstat(&d, NUM(fid->qid.path), Qdir);
-		m = convD2M(&d, &buf[n], blen-n);
-		if(off <= pos){
-			if(m <= BIT16SZ || m > cnt)
-				break;
-			n += m;
-			cnt -= m;
-		}
-		pos += m;
-	}
-	return n;
-}
-
-int
-readdir(Fid *f, uchar *buf, long off, int cnt, int blen)
-{
-	int i, m, n;
-	long pos;
-	Dir d;
-
-	n = 0;
-	pos = 0;
-	for (i = 0; i < Qtop; i++){
-		mkstat(&d, NUM(f->qid.path), i);
-		m = convD2M(&d, &buf[n], blen-n);
-		if(off <= pos){
-			if(m <= BIT16SZ || m > cnt)
-				break;
-			n += m;
-			cnt -= m;
-		}
-		pos += m;
-	}
-	return n;
-}
-
-void
-readbuf(char *s, long n)
-{
-	rhdr.count = thdr.count;
-	if(thdr.offset >= n){
-		rhdr.count = 0;
-		return;
-	}
-	if(thdr.offset+rhdr.count > n)
-		rhdr.count = n - thdr.offset;
-	rhdr.data = s + thdr.offset;
-}
-
-char*
-rread(Fid *f)
-{
-	long off;
-	int n, cnt, t;
-
-	rhdr.count = 0;
-	off = thdr.offset;
-	cnt = thdr.count;
-
-	if(cnt > messagesize - IOHDRSZ)
-		cnt = messagesize - IOHDRSZ;
-
-	rhdr.data = (char*)mbuf;
-
-	n = 0;
-	t = FILE(f->qid.path);
-	switch(t){
-	case Qtop:
-		n = readtopdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
-		rhdr.count = n;
-		return nil;
-	case Qclassical:
-		n = readclasdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
-		rhdr.count = n;
-		return nil;
-	case Qdir:
-	case Qroot:
-		n = readdir(f, mbuf, off, cnt, messagesize - IOHDRSZ);
-		rhdr.count = n;
-		return nil;
-	case Qctl:
-		snprint(bigbuf, sizeof bigbuf, "%d objects in tree (%d holes)\n", notab, hotab);
-		break;
-	case Qchildren:
-	case Qdigest:
-	case Qfiles:
-	case Qfulltext:
-	case Qkey:
-	case Qminiparentage:
-	case Qparent:
-	case Qparentage:
-	case Qtext:
-	case Qtype:
-		n = fileinfo(bigbuf, sizeof bigbuf, NUM(f->qid.path), t);
-		break;
-	default:
-		sysfatal("rread: %d", t);
-	}
-	readbuf(bigbuf, n);
-	return nil;
-}
-
-char*
-rwrite(Fid *f)
-{
-	long cnt;
-	char *p;
-
-	if(FILE(f->qid.path) != Qctl)
-		return Eperm;
-	rhdr.count = 0;
-	cnt = thdr.count;
-	if(p = strchr(thdr.data, '\n'))
-		*p = '\0';
-	if(strncmp(thdr.data, "quit", cnt) == 0)
-		threadexitsall(nil);
-	else if(strncmp(thdr.data, "reread", cnt) == 0)
-		reread();
-	else
-		return "illegal command";
-	rhdr.count = thdr.count;
-	return nil;
-}
-
-char *
-rclunk(Fid *f)
-{
-	f->flags &= ~(Open|Busy);
-	return 0;
-}
-
-char *
-rremove(Fid *)
-{
-	return Eperm;
-}
-
-char *
-rstat(Fid *f)
-{
-	Dir d;
-
-	mkstat(&d, NUM(f->qid.path), FILE(f->qid.path));
-	rhdr.nstat = convD2M(&d, mbuf, messagesize - IOHDRSZ);
-	rhdr.stat = mbuf;
-	return 0;
-}
-
-char *
-rwstat(Fid*)
-{
-	return Eperm;
-}
-
-Fid *
-newfid(int fid)
-{
-	Fid *f, *ff;
-
-	ff = nil;
-	for(f = fids; f; f = f->next)
-		if(f->fid == fid){
-			return f;
-		}else if(ff == nil && (f->flags & Busy) == 0)
-			ff = f;
-	if(ff == nil){
-		ff = malloc(sizeof *ff);
-		if (ff == nil)
-			sysfatal("malloc: %r");
-		memset(ff, 0, sizeof *ff);
-		ff->next = fids;
-		fids = ff;
-	}
-	ff->fid = fid;
-	return ff;
-}
-
-void
-io(void *)
-{
-	char *err;
-	int n;
-	extern int p[];
-	Fid *f;
-
-	threadsetname("file server");
-	close(p[1]);
-	while((n = read9pmsg(mfd[0], mdata, messagesize)) != 0){
-		if(n < 0){
-			char e[32];
-			rerrstr(e, sizeof e);
-			if (strcmp(e, "interrupted") == 0){
-				if (debug & DbgFs) fprint(2, "read9pmsg interrupted\n");
-				continue;
-			}
-			sysfatal("mount read: %s", e);
-		}
-		if(convM2S(mdata, n, &thdr) != n)
-			sysfatal("convM2S format error: %r");
-
-		if(debug & DbgFs)
-			fprint(2, "io:<-%F\n", &thdr);
-
-		rhdr.data = (char*)mbuf;
-
-		if(!fcalls[thdr.type])
-			err = "bad fcall type";
-		else {
-			f = newfid(thdr.fid);
-			err = (*fcalls[thdr.type])(f);
-		}
-		if(err){
-			rhdr.type = Rerror;
-			rhdr.ename = err;
-		}else{
-			rhdr.type = thdr.type + 1;
-			rhdr.fid = thdr.fid;
-		}
-		rhdr.tag = thdr.tag;
-		if(debug & DbgFs)
-			fprint(2, "io:->%F\n", &rhdr);/**/
-		n = convS2M(&rhdr, mdata, messagesize);
-		if(write(mfd[1], mdata, n) != n)
-			sysfatal("mount write");
-	}
-	threadexitsall("die yankee pig dog");
-}
-
-int
-newid(void)
-{
-	int rv;
-	static int id;
-	static Lock idlock;
-
-	lock(&idlock);
-	rv = ++id;
-	unlock(&idlock);
-
-	return rv;
-}
--- a/sys/src/games/music/jukefs/mk.dep	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,6 +0,0 @@
-catset.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/bio.h object.h catset.h
-fs.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/bio.h /sys/include/fcall.h object.h
-parse.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/bio.h /sys/include/ctype.h object.h catset.h parse.h
-print.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/ctype.h /sys/include/bio.h /sys/include/thread.h object.h parse.h catset.h
-search.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/bio.h /sys/include/thread.h object.h parse.h search.h
-server.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/bio.h /sys/include/fcall.h object.h parse.h print.h catset.h
--- a/sys/src/games/music/jukefs/mkfile	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,18 +0,0 @@
-</$objtype/mkfile
-<../mkinc
-
-CFLAGS = -DDEFAULTMAP="/sys/lib/music/map"
-TARG = jukefs
-BIN = /$objtype/bin/games
-
-CFILES=\
-	catset.c\
-	fs.c\
-	parse.c\
-	print.c\
-	search.c\
-	server.c\
-
-OFILES = ${CFILES:%.c=%.$O}
-
-</sys/src/cmd/mkone
--- a/sys/src/games/music/jukefs/object.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,117 +0,0 @@
-/* Keywords */
-
-typedef enum {
-	Category,
-	Cddata,
-	Cmd,
-	File,
-	Include,
-	Key,
-	Lyrics,
-	Part,
-	Path,
-	Performance,
-	Recording,
-	Root,
-	Search,
-	Soloists,
-	Time,
-	Track,
-	Work,
-	Ntoken,	/* Initializer for ntoken */
-	Eof	=	-1,
-	Txt	=	-2,
-	BraceO	=	-3,
-	BraceC	=	-4,
-	Equals	=	-5,
-	Newcat	=	-6,
-} Type;
-
-typedef struct Object Object;
-typedef struct Catset Catset;
-typedef struct Token Token;
-typedef struct Cmdlist Cmdlist;
-
-/* Token-only types */
-
-typedef enum {
-	Obj,
-	Cat,
-} Kind;
-
-struct Catset {
-	uchar *bitpiece;	/* mallocated */
-	int nbitpiece;
-};
-
-
-struct Token {
-	char	*name;
-	Kind	kind;
-	long	value;
-	char	*kname;
-	Catset	categories;
-};
-
-typedef enum {
-	Hierarchy,
-	Typelist,
-	Nlisttype,
-} Listtype;
-
-struct Cmdlist {
-	int	flag;
-	char	*name;
-};
-
-#define KEYLEN 128
-
-struct Object {
-	Type	type;
-	int	tabno;		/* entry in object table */
-	Object	*parent;
-	Object	**children;	/* mallocated */
-	Object	**catparents;
-	Object	*orig;		/* back pointer from search object */
-	int	nchildren;
-	int	ncatparents;
-	Catset	categories;	/* was int */
-	int	flags;
-	int	num;		/* for enumerations */
-	char	*value;		/* mallocated */
-	char	key[KEYLEN];
-	char	*path;		/* mallocated */
-};
-
-#define Sort	0x01
-#define Enum	0x02
-#define Hier	0x04
-#define Elab	0x10	/* elaborated rune string */
-
-extern	Token	*tokenlist;
-extern	int	ncat;
-extern	Object	**catobjects;
-extern	Biobuf	*f;
-extern	char	*file;
-extern	Object	*root;
-extern	int	ntoken;
-
-extern	Object	**otab;	// object table
-extern	int	notab;	// no of entries used
-extern	int	sotab;	// no of entries mallocated
-extern	int	hotab;	// no of holes in tab
-extern	char	*user;
-
-void	io(void *);
-long	printchildren(char*, int, Object*);
-long	printdigest(char*, int, Object*);
-long	printfiles(char*, int, Object*);
-long	printfulltext(char*, int, Object*);
-long	printkey(char*, int, Object*);
-long	printminiparentage(char*, int, Object*);
-long	printparent(char*, int, Object*);
-long	printparentage(char*, int, Object*);
-long	printtext(char*, int, Object*);
-long	printtype(char*, int, Object*);
-void	reread(void);
-void	listfiles(Object *o);
--- a/sys/src/games/music/jukefs/parse.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,616 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <bio.h>
-#include <ctype.h>
-#include "object.h"
-#include "catset.h"
-#include "parse.h"
-
-#define MAXTOKEN 1024
-
-Biobuf *f;
-static int str;
-char *file;
-
-Token tokenlistinit[] = {
-	{ "category",	Obj,	Category	, "music"	, {nil,0}},
-	{ "cddata",	Obj,	Cddata		, nil		, {nil,0}},
-	{ "command",	Obj,	Cmd		, nil		, {nil,0}},
-	{ "file",	Obj,	File		, "file"	, {nil,0}},
-	{ "include",	Obj,	Include		, nil		, {nil,0}},
-	{ "key",	Obj,	Key		, nil		, {nil,0}},
-	{ "lyrics",	Obj,	Lyrics		, "lyrics"	, {nil,0}},
-	{ "part",	Obj,	Part		, "title"	, {nil,0}},
-	{ "path",	Obj,	Path		, nil		, {nil,0}},
-	{ "performance",Obj,	Performance	, "artist"	, {nil,0}},
-	{ "recording",	Obj,	Recording	, "title"	, {nil,0}},
-	{ "root",	Obj,	Root		, nil		, {nil,0}},
-	{ "search",	Obj,	Search		, nil		, {nil,0}},
-	{ "soloists",	Obj,	Soloists	, "artist"	, {nil,0}},
-	{ "time",	Obj,	Time		, "time"	, {nil,0}},
-	{ "track",	Obj,	Track		, "title"	, {nil,0}},
-	{ "work",	Obj,	Work		, "title"	, {nil,0}},
-};
-Token *tokenlist;
-int ntoken = nelem(tokenlistinit);
-int catnr = 0;
-
-Cmdlist cmdlist[] = {
-	{	Sort,	"sort"		},
-	{	Enum,	"number"	},
-	{	0x00,	0		},
-};
-
-static char *curtext;
-
-void
-inittokenlist(void)
-{
-	int i;
-
-	ntoken = nelem(tokenlistinit);
-	tokenlist = malloc(sizeof(tokenlistinit));
-	memmove(tokenlist, tokenlistinit, sizeof(tokenlistinit));
-	for(i = 0; i< ntoken; i++){
-		tokenlist[i].name = strdup(tokenlist[i].name);
-		catsetinit(&tokenlist[i].categories, tokenlist[i].value);
-	}
-	curtext = smprint("{");
-}
-
-Type
-gettoken(char *token)
-{
-	char *p, *q;
-	int i, n;
-	Token *t;
-
-	for(;;){
-		if(curtext){
-			p = &curtext[strspn(curtext, " \t")];	
-			if(*p && *p != '\n')
-				break;
-		}
-		do {
-			str++;
-			free(curtext);
-			if((curtext = Brdstr(f, '\n', 0)) == nil)
-				return Eof;
-		} while(curtext[0] == '#');
-	}
-	if(*p == '{'){
-		*token++ = *p;
-		*token = 0;
-		*p = ' ';
-		return BraceO;
-	}
-	if(*p == '}'){
-		*token++ = *p;
-		*token = 0;
-		*p = ' ';
-		return BraceC;
-	}
-	if(*p == '='){
-		*token++ = *p;
-		*token = 0;
-		*p = ' ';
-		return Equals;
-	}
-	t = nil;
-	n = 0;
-	for(i = 0; i < ntoken; i++){
-		t = &tokenlist[i];
-		if(strncmp(p, t->name, n=strlen(t->name)) == 0){
-			q = &p[n];
-				if(isalnum(*q) || *q == '-') continue;
-			q += strspn(q, " \t");
-			if(t->kind == Obj && *q == '{')
-				break;
-			if(t->kind == Cat && *q == '=')
-				break;
-		}
-	}
-	if(i < ntoken){
-		strcpy(token, t->name);
-		memset(p, ' ', n);
-		return i;
-	}
-	assert(strlen(token) < MAXTOKEN);
-	if(strchr(p, '{'))
-		sysfatal("Illegal keyword or parse error: %s", p);
-	if((q = strchr(p, '='))){
-		if(q == p) goto tx;
-		*q = 0;
-		strcpy(token, p);
-		assert(strlen(token) < MAXTOKEN);
-		memset(p, ' ', q-p);
-		*q = '=';
-		for(q = token; *q; q++)
-			if(!isalnum(*q) && !isspace(*q)) break;
-		if(*q) return Txt;
-		while(isspace(*--q)) *q = 0;
-		return Newcat;
-	}
-tx:	if((q = strchr(p, '}'))){
-		*q = 0;
-		strcpy(token, p);
-		assert(strlen(token) < MAXTOKEN);
-		memset(p, ' ', q-p);
-		*q = '}';
-		return Txt;
-	}
-	strcpy(token, p);
-	assert(strlen(token) < MAXTOKEN);
-	free(curtext);
-	curtext = nil;
-	return Txt;
-}
-
-Object *
-getobject(Type t, Object *parent)
-{
-	char *token;
-	char *textbuf;
-	char *tp, *p, *q;
-	int i;
-	Object *o, *oo, *child;
-	Token *ot;
-	Type nt;
-
-	token = malloc(MAXTOKEN);
-	textbuf = malloc(8192);
-
-	tp = textbuf;
-	o = newobject(t, parent);
-	o->flags |= Hier;
-	if(parent == nil){
-		root = o;
-		o->path = strdup(startdir);
-		setmalloctag(o->path, 0x100001);
-	}
-	if(gettoken(token) != BraceO)
-		sysfatal("Parse error: no brace, str %d", str);
-	for(;;){
-		t = gettoken(token);
-		if(t >= 0)
-			switch(tokenlist[t].kind){
-			case Obj:
-				switch(t){
-				case Key:
-				case Cmd:
-				case Path:
-					if(getobject(t, o) != nil)
-						sysfatal("Non-null child?");
-					break;
-				case Include:
-				case Category:
-					child = getobject(t, o);
-					if(child) addchild(o, child, "case Category");
-					break;
-				default:
-					/* subobject */
-					child = getobject(t, o);
-					if(child == nil)
-						sysfatal("Null child?");
-					addchild(o, child, "default");
-					break;
-				}
-				break;
-			case Cat:
-			catcase:    nt = gettoken(token);
-				if(nt != Equals)
-					sysfatal("Expected Equals, not %s", token);
-				nt = gettoken(token);
-				if(nt != Txt)
-					sysfatal("Expected Text, not %s", token);
-				if((p = strchr(token, '\n'))) *p = 0;
-				p = token;
-				if(o->type == Category){
-					if(catsetisset(&o->categories)){
-						fprint(2, "Category object must have one category\n");
-					}
-					catsetcopy(&o->categories, &tokenlist[t].categories);
-					strncpy(o->key, p, KEYLEN);
-					if(catobjects[t] == 0)
-						sysfatal("Class %s not yet defined", tokenlist[t].name);
-					for(i = 0; i < catobjects[t]->nchildren; i++)
-						if(strcmp(catobjects[t]->children[i]->key, p) == 0)
-							break;
-					if(i == catobjects[t]->nchildren){
-						/* It's a new key for the category */
-						addchild(catobjects[t], o, "new key for cat");
-					}else{
-						/* Key already existed */
-						oo = catobjects[t]->children[i];
-						if(oo->value)
-							sysfatal("Duplicate category object for %s", oo->value);
-						catobjects[t]->children[i] = o;
-						if(oo->nchildren){
-							for(i = 0; i < oo->nchildren; i++){
-								if(oo->children[i]->parent == oo)
-									oo->children[i]->parent = o;
-								addchild(o, oo->children[i], "key already existed");
-							}
-						}
-						freeobject(oo, "a");
-					}
-					o->parent = catobjects[t];
-				}else{
-					catsetorset(&o->categories, &tokenlist[t].categories);
-					for(i = 0; i < catobjects[t]->nchildren; i++)
-						if(strcmp(catobjects[t]->children[i]->key, p) == 0)
-							break;
-					if(i == catobjects[t]->nchildren){
-						oo = newobject(Category, catobjects[t]);
-/*
-						oo->value = strdup(token);
-*/
-						strncpy(oo->key, p, KEYLEN);
-						catsetcopy(&oo->categories, &tokenlist[t].categories);
-						addchild(catobjects[t], oo, "catobjects[t],oo");
-					}
-					addchild(catobjects[t]->children[i], o, "children[i]");
-				}
-				break;
-			}
-		else
-			switch(t){
-			case Eof:
-				if(o->type == Root){
-					free(token);
-					free(textbuf);
-					return o;
-				}
-				sysfatal("Unexpected Eof in %s, file %s", tokenlist[o->type].name, file);
-			case Newcat:
-				/* New category, make an entry in the tokenlist */
-				tokenlist = realloc(tokenlist, (ntoken+1)*sizeof(Token));
-				ot = &tokenlist[ntoken];
-				ot->name = strdup(token);
-				setmalloctag(ot->name, 0x100002);
-				ot->kind = Cat;
-				ot->value = -1;
-				memset(&ot->categories, 0, sizeof(Catset));
-				catsetinit(&ot->categories, catnr++);
-				/* And make an entry in the catobjects table */
-				if(ncat <= ntoken){
-					catobjects = realloc(catobjects, (ntoken+1)*sizeof(Object*));
-					while(ncat <= ntoken) catobjects[ncat++] = nil;
-				}
-				if(catobjects[ntoken] != nil)
-					sysfatal("Class %s already defined in %s:%d", token, file, str);
-				if(0) fprint(2, "newcat: token %s catnr %d ntoken %d ncat %d\n",
-					token, catnr, ntoken, ncat);
-				catobjects[ntoken] = newobject(Category, root);
-				if(o->type == Category)
-					catobjects[ntoken]->flags = o->flags&Hier;
-				catobjects[ntoken]->flags |= Sort;
-				strncpy(catobjects[ntoken]->key, token, KEYLEN);
-				catsetcopy(&catobjects[ntoken]->categories, &ot->categories);
-				addchild(root, catobjects[ntoken], "root");
-				t = ntoken;
-				ntoken++;
-				goto catcase;
-			case Txt:
-				strcpy(tp, token);
-				tp += strlen(token);
-				break;
-			case BraceC:
-				while(tp > textbuf && tp[-1] == '\n') *--tp = 0;
-				if((o->type == File || o->type == Include) && o->path){
-					o->value = smprint("%s/%s", o->path, textbuf);
-				}else if(tp > textbuf){
-					o->value = strdup(textbuf);
-					setmalloctag(o->value, 0x100003);
-				}
-				switch(o->type){
-				case Cmd:
-					q = strtok(o->value, " \t,;\n");
-					while(q){
-						if(*q) for(i = 0; cmdlist[i].name; i++){
-							if(strcmp(q, cmdlist[i].name) == 0){
-								o->parent->flags |= cmdlist[i].flag;
-								break;
-							}
-							if(cmdlist[i].name == 0)
-								fprint(2, "Unknown command: %s\n", q);
-						}
-						q = strtok(nil, " \t,;\n");
-					}
-					freeobject(o, "b");
-					free(token);
-					free(textbuf);
-					return nil;
-				case Path:
-					p = o->value;
-					free(o->parent->path);
-					if(p[0] == '/' || o->path == nil){
-						o->parent->path = strdup(p);
-						setmalloctag(o->parent->path, 0x100004);
-					}else{
-						o->parent->path = smprint("%s/%s", o->path, p);
-						setmalloctag(o->parent->path, 0x100005);
-					}
-					freeobject(o, "b");
-					free(token);
-					free(textbuf);
-					return nil;
-				case Include:
-					free(token);
-					free(textbuf);
-					return getinclude(o);
-				case Category:
-				/*
-					if(o->nchildren) break;
-				 */
-					free(token);
-					free(textbuf);
-					return nil;
-				case Key:
-					strncpy(o->parent->key, o->value, KEYLEN);
-					freeobject(o, "d");
-					free(token);
-					free(textbuf);
-					return nil;
-				default:
-					break;
-				}
-				free(token);
-				free(textbuf);
-				return o;
-			default:
-				fprint(2, "Unexpected token: %s\n", token);
-				free(token);
-				free(textbuf);
-				return nil;
-			}
-	}
-}
-
-Object *
-getinclude(Object *o)
-{
-		char *savetext;
-		Biobuf *savef = f;
-		char *savefile, fname[256];
-		Object *oo;
-		int savestr = str;
-		char token[MAXTOKEN], *dirname, *filename;
-		Type t;
-
-		str = 0;
-		if(curtext){
-			savetext = strdup(curtext);
-			setmalloctag(savetext, 0x100006);
-		}else
-			savetext = nil;
-		if((f = Bopen(o->value, OREAD)) == nil)
-			sysfatal("getinclude: %s: %r", o->value);
-		savefile = file;
-		file = strdup(o->value);
-		strncpy(fname, o->value, 256);
-		if((filename = strrchr(fname, '/'))){
-			*filename = 0;
-			dirname = fname;
-			filename++;
-		}else{
-			dirname = "";
-			filename = fname;
-		}
-		while((t = gettoken(token)) != Eof){
-			if(t < 0){
-				if(*dirname)
-					sysfatal("Bad include file %s/%s, token %s, str %d",
-						dirname, filename, token, str);
-				else
-					sysfatal("Bad include file %s, token %s, str %d",
-						filename, token, str);
-			}
-			free(o->path);
-			o->path = strdup(dirname);
-			setmalloctag(o->path, 0x100007);
-			oo = getobject(t, o->parent);
-			if(oo) addchild(o->parent, oo, "o->parent, oo");
-		}
-		freeobject(o, "e");
-		free(curtext);
-		curtext = nil;
-		if(savetext)
-			curtext = savetext;
-		free(file);
-		file = savefile;
-		str = savestr;
-		Bterm(f);
-		f = savef;
-		return nil;
-}
-
-void
-addchild(Object *parent, Object *child, char *where)
-{
-		int i;
-
-		/* First check if child's already been added
-		 * This saves checking elsewhere
-		 */
-		for(i = 0; i < parent->nchildren; i++)
-				if(parent->children[i] == child) return;
-		parent->children = realloc(parent->children, (i+1)*sizeof(Object*));
-		parent->children[i] = child;
-		parent->nchildren++;
-		if(parent->type == Category && child->type == Category)
-			return;
-		if(parent->type == Work && child->type == Work)
-			return;
-		if(parent->type == Work && child->type == Track)
-			return;
-		if(parent->type == Track && child->type == File)
-			return;
-		if(child->parent == child)
-			return;
-		if(parent->type == Root)
-			return;
-		if(parent->parent->type == Root)
-			return;
-//		addcatparent(parent, child);
-		i = child->ncatparents;
-		if(0) fprint(2, "addcatparent %s parent %d type %d child %d type %d\n",where,
-			parent->tabno, parent->type, child->tabno, child->type);
-		child->catparents = realloc(child->catparents, (i+1)*sizeof(Object*));
-		child->catparents[i] = parent;
-		child->ncatparents++;
-}
-
-void
-addcatparent(Object *parent, Object *child)
-{
-		int i;
-
-		/* First check if child's already been added
-		 * This saves checking elsewhere
-		 */
-		if(child->parent == child)
-			return;
-//		for(i = 0; i < child->ncatparents; i++)
-//				if(child->catparents[i] == parent) return;
-		i = child->ncatparents;
-		fprint(2, "addcatparent parent %d child %d\n", parent->tabno, child->tabno);
-		child->catparents = realloc(child->catparents, (i+1)*sizeof(Object*));
-		child->catparents[i] = parent;
-		child->ncatparents++;
-}
-
-void
-sortprep(char *out, int n, Object *o)
-{
-	char *p, *q;
-
-	if(*o->key)
-		q = o->key;
-	else if (o->value)
-		q = o->value;
-	else
-		q = "";
-	if(p = strchr(q, '~'))
-		p++;
-	else
-		p = q;
-	for(q = out; *p && q < out+n-1; q++)
-		*q = tolower(*p++);
-	*q = 0;
-}
-
-void
-childsort(Object *o)
-{
-		Object *oo;
-		int i, j, n;
-		char si[256], sj[256];
-		/* sort the kids by key or by value */
-
-		n = o->nchildren;
-		if(n > 1){
-			for(i = 0; i < n-1; i++){
-				sortprep(si, nelem(si), o->children[i]);
-				for(j = i+1; j < n; j++){
-					sortprep(sj, nelem(sj), o->children[j]);
-					if(strncmp(si, sj, sizeof(si)) > 0){
-						oo = o->children[i];
-						o->children[i] = o->children[j];
-						o->children[j] = oo;
-						strncpy(si, sj, sizeof(si));
-					}
-				}
-			}
-		}
-}
-
-void
-childenum(Object *o){
-		Object *oo;
-		int i, n = 1;
-
-		for(i = 0; i < o->nchildren; i++){
-			oo = o->children[i];
-			if(tokenlist[oo->type].kind == Cat)
-				oo->num = n++;
-			else
-				switch(oo->type){
-				case Category:
-				case Part:
-				case Recording:
-				case Track:
-				case Work:
-					oo->num = n++;
-				default:
-					break;
-				}
-		}
-}
-
-Object *
-newobject(Type t, Object *parent){
-	Object *o;
-	int tabno;
-
-	if(hotab){
-		for(tabno = 0; tabno < notab; tabno++)
-			if(otab[tabno] == nil)
-				break;
-		if(tabno == notab)
-			sysfatal("lost my hole");
-		hotab--;
-	}else{
-		if(sotab < notab+1){
-			sotab += 512;
-			otab = realloc(otab, sizeof(Object*)*sotab);
-			if(otab == nil)
-				sysfatal("realloc: %r");
-		}
-		tabno = notab++;
-	}
-	o = mallocz(sizeof(Object), 1);
-	o->tabno = tabno;
-	otab[tabno] = o;
-	o->type = t;
-	o->parent = parent;
-	if(parent && parent->path){
-		o->path = strdup(parent->path);
-		setmalloctag(o->path, 0x100008);
-	}
-	return o;
-}
-
-void
-freeobject(Object *o, char*){
-
-	free(o->children);
-	if(o->orig == nil)
-		free(o->value);
-	free(o->path);
-	free(o->catparents);
-	catsetfree(&o->categories);
-	otab[o->tabno] = nil;
-	hotab++;
-	free(o);
-}
-
-void
-freetree(Object *o)
-{
-	int i;
-
-	for(i = 0; i < o->nchildren; i++)
-		if(o->children[i]->parent == o)
-			freetree(o->children[i]);
-	free(o->children);
-	if(o->orig == nil)
-		free(o->value);
-	free(o->path);
-	free(o->catparents);
-	catsetfree(&o->categories);
-	otab[o->tabno] = nil;
-	hotab++;
-	free(o);
-}
--- a/sys/src/games/music/jukefs/parse.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,15 +0,0 @@
-Object	*getobject(Type, Object *);
-Object	*getinclude(Object *);
-void	childsort(Object *);
-void	childenum(Object *);
-Object	*newobject(Type, Object *);
-void	freeobject(Object *, char *);
-void	freetree(Object *);
-void	*mymalloc(void *old, int size);
-void	addchild(Object *, Object *, char*);
-void	addcatparent(Object *, Object *);
-void	inittokenlist(void);
-void	initparse(void);
-void	exit(int);
-
-extern char *startdir;
--- a/sys/src/games/music/jukefs/print.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,489 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <ctype.h>
-#include <bio.h>
-#include <thread.h>
-#include "object.h"
-#include "parse.h"
-#include "catset.h"
-
-int	fflag;
-
-void
-listfiles(Object *o)
-{
-	int i;
-
-	if(o->type == File){
-		print("%s\n", o->value);
-		return;
-	}
-	for(i = 0; i < o->nchildren; i++)
-		if(o->children[i]->parent == o)
-			listfiles(o->children[i]);
-}
-
-int
-indent(char *lp, int ln, int n, char *buf) {
-	int sln;
-	char *p, c;
-
-	sln = ln;
-	if (ln <= 0)
-		return 0;
-	if (n < 0)
-		n = -n;
-	else {
-		if (ln < 4*n)
-			goto out;
-		memset(lp, ' ', 4*n);
-		lp += 4*n;
-		ln -= 4*n;
-	}
-	if(p = buf) while (ln > 1) {
-		c = *p++;
-		if(c == '\0')
-			break;
-		if(c == '~')
-			continue;
-		*lp++ = c;
-		ln--;
-		if (c == '\n' && p[1]) {
-			if (ln < 4*n)
-				break;
-			memset(lp, ' ', 4*n);
-			lp += 4*n;
-			ln -= 4*n;
-		}
-	}
-	*lp = '\0';
-out:
-	return sln - ln;
-}
-
-long
-printchildren(char *lp, int ln, Object *o) {
-	int i, r;
-	char *sp;
-
-	sp = lp;
-	if (o->flags & Sort) {
-		childsort(o);
-		o->flags &= ~Sort;
-	}
-	for(i = 0; i < o->nchildren && ln > 0; i++){
-		r = snprint(lp, ln, "%d\n", o->children[i]->tabno);
-		lp += r;
-		ln -= r;
-	}
-	return lp - sp;
-}
-
-long
-printminiparentage(char *lp, int ln, Object *o) {
-	char *p, c;
-	int r, sln;
-
-	if (ln <= 0) return 0;
-	*lp = '\0';
-	if (o == 0 || o->type == Root)
-		return 0;
-	sln = ln;
-	if(o->orig) o = o->orig;
-	r = printminiparentage(lp, ln, o->parent);
-	lp += r;
-	ln -= r;
-	if (ln <= 0) return 0;
-	if(o->value && o->type != File){
-		if(r && o->value && ln > 1){
-			*lp++ = '/';
-			ln--;
-		}
-		p = o->value;
-		while(ln > 0){
-			c = *p++;
-	    		if(c == '\n' || c == '\0')
-	    			break;
-			if(c == '~')
-				continue;
-			*lp++ = c;
-			ln--;
-		}
-	}
-	if(ln > 0)
-		*lp = '\0';
-	return sln - ln;
-}
-
-long
-printparentage(char *lp, int ln, Object *o) {
-	int i;
-	int r, k, sln;
-
-	if(ln <= 0)
-		return 0;
-	*lp = '\0';
-	if(o == 0 || o->type == Root)
-		return 0;
-	if(0)fprint(2, "parentage 0x%p %d type %d value 0x%p parent 0x%p %d\n", o, o->tabno, o->type, o->value, o->parent, o->parent->tabno);
-	if(o->orig){
-		if(0)fprint(2, "parentage 0x%p %d type %d orig %d type %d parent 0x%p %d\n", o, o->tabno, o->type, o->orig->tabno, o->orig->type, o->orig->parent, o->orig->parent->tabno);
-		o = o->orig;
-	}
-	sln = ln;
-	r = printparentage(lp, ln, o->parent);
-	lp += r; ln -= r;
-	if(o->type == File && fflag == 0){
-		if(ln > 0)
-			*lp = '\0';
-		return sln - ln;
-	}
-	if(o->value && *o->value && ln > 0){
-		if(o->type == Category){
-			if(o->parent == root){
-				r = snprint(lp, ln, "category: ");
-				lp += r; ln -= r;
-			}else{
-				for(k = Ntoken; k < ntoken; k++)
-					if(catseteq(&o->categories,&tokenlist[k].categories)){
-						r = snprint(lp, ln, "%s: ", tokenlist[k].name);
-						lp += r; ln -= r;
-						break;
-					}
-			}
-		}else{
-			r = snprint(lp, ln, "%s: ", tokenlist[o->type].name);
-			lp += r; ln -= r;
-		}
-		if(ln <= 0)
-			return sln - ln;
-		if(o->num){
-			r = snprint(lp, ln, "%2d. ", o->num);
-			lp += r; ln -= r;
-		}
-		if(ln <= 0)
-			return sln - ln;
-		r = indent(lp, ln, -1, o->value);
-		lp += r; ln -= r;
-		if(ln > 1){
-			*lp++ = '\n';
-			ln--;
-		}
-	}else{
-		if(0)fprint(2, "parentage 0x%p %d type %d no value\n", o, o->tabno, o->type);
-	}
-	for(i = 0; i < o->nchildren && ln > 0; i++)
-		switch(o->children[i]->type){
-		case Performance:
-		case Soloists:
-		case Lyrics:
-			r = snprint(lp, ln, "%s: ", tokenlist[o->children[i]->type].name);
-			lp += r; ln -= r;
-			if(ln <= 0)
-				break;
-			r = indent(lp, ln, -1, o->children[i]->value);
-			lp += r; ln -= r;
-			if(ln > 1){
-				*lp++ = '\n';
-				ln--;
-			}
-			break;
-		case Time:
-			r = snprint(lp, ln, "%s: %s\n", "duration", o->children[i]->value);
-			lp += r; ln -= r;
-			break;
-		case File:
-			if(fflag){
-				r = snprint(lp, ln, "%s: %s\n", "file", o->children[i]->value);
-				lp += r; ln -= r;
-			}
-			break;
-		default:
-			break;
-		}
-	if(ln > 0)
-		*lp = '\0';
-	return sln - ln;
-}
-
-long
-printparent(char *lp, int ln, Object *o) {
-	return snprint(lp, ln, "%d", o->parent->tabno);
-}
-
-long
-printkey(char *lp, int ln, Object *o) {
-	return snprint(lp, ln, "%s", o->key?o->key:o->value);
-}
-
-long
-printtype(char *lp, int ln, Object *o) {
-	return snprint(lp, ln, "%s", tokenlist[o->type].name);
-}
-
-long
-printtext(char *lp, int ln, Object *o) {
-	return snprint(lp, ln, "%s", o->value?o->value:o->key);
-}
-
-long
-printfulltext(char *lp, int ln, Object *o) {
-	char *sp, *p, *q;
-	int i, j, k, c, depth;
-	Object *oo;
-
-	depth = 0;
-	sp = lp;
-	switch(o->type){
-	case Category:
-		if(o->parent == root){
-			j = snprint(lp, ln, "category:");
-			lp += j; ln -= j;
-		}else{
-			for(k = Ntoken; k < ntoken; k++)
-				if(catseteq(&o->categories, &tokenlist[k].categories)){
-					j = snprint(lp, ln, "%s:", tokenlist[k].name);
-					lp += j; ln -= j;
-					break;
-				}
-		}
-		if(ln <= 0)
-			return lp - sp;
-		p = o->value;
-		if(p == nil)
-			p = o->key;
-		if((q = strchr(p, '\n')) && q[1] != '\0'){
-			// multiple lines
-			*lp++ = '\n'; ln--;
-			if(ln <= 0)
-				break;
-			j = indent(lp, ln, depth+1, p);
-			lp += j; ln -= j;
-		}else{
-			*lp++ = ' '; ln--;
-			while((c=*p++) && ln > 0){
-				if(c == '~')
-					continue;
-				*lp++ = c;
-				ln--;
-				if(c == '\n')
-					break;
-			}
-		}
-		break;
-	case Track:
-	case Part:
-	case Recording:
-	case Root:
-	case Search:
-	case Work:
-		j = snprint(lp, ln, "%s:", tokenlist[o->type].name);
-		lp += j; ln -= j;
-		if(ln <= 0)
-			break;
-		if(o->num){
-			j = snprint(lp, ln, " %2d.", o->num);
-			lp += j; ln -= j;
-		}
-		if(ln <= 0)
-			break;
-		p = o->value;
-		if(p == nil)
-			p = o->key;
-		if((q = strchr(p, '\n')) && q[1] != '\0'){
-			// multiple lines
-			*lp++ = '\n'; ln--;
-			if(ln <= 0)
-				break;
-			j = indent(lp, ln, depth+1, p);
-			lp += j; ln -= j;
-		}else{
-			*lp++ = ' '; ln--;
-			while((c =*p++) && ln > 0){
-				if(c == '~')
-					continue;
-				*lp++ = c;
-				ln--;
-				if(c == '\n')
-					break;
-			}
-		}
-	default:
-		break;
-	}
-	depth++;
-	for(i = 0; i < o->nchildren && ln > 0; i++){
-		oo = o->children[i];
-		switch(oo->type){
-		case Lyrics:
-		case Performance:
-		case Soloists:
-		case Time:
-			if (ln <= 4*depth + 1)
-				break;
-			*lp++ = '\n'; ln--;
-			memset(lp, ' ', 4*depth);
-			lp += 4*depth;
-			ln -= 4*depth;
-			if(ln <= 0)
-				break;
-			j = snprint(lp, ln, "%s:", tokenlist[oo->type].name);
-			lp += j; ln -= j;
-			if(ln <= 0)
-				break;
-			p = oo->value;
-			if(ln <= 1)
-				break;
-			if((q = strchr(p, '\n')) && q[1] != '\0'){
-				// multiple lines
-				*lp++ = '\n'; ln--;
-				j = indent(lp, ln, depth+1, p);
-				lp += j; ln -= j;
-			}else{
-				*lp++ = ' '; ln--;
-				while((c =*p++) && ln > 0){
-					if(c == '~')
-						continue;
-					*lp++ = c;
-					ln--;
-					if(c == '\n')
-						break;
-				}
-			}
-		}
-	}
-	*lp = '\0';
-	return lp - sp;
-}
-
-long
-printfiles(char *lp, int ln, Object *o) {
-	int i, r;
-	char *sp;
-
-	sp = lp;
-	if (o->type == File)
-		lp += snprint(lp, ln, "%d	%s\n", o->tabno, o->value);
-	else {
-		for (i = 0; i < o->nchildren && ln > 0; i++){
-			r = printfiles(lp, ln, o->children[i]);
-			lp += r;
-			ln -= r;
-		}
-	}
-	return lp - sp;
-}
-
-long
-printdigest(char *lp, int ln, Object *o) {
-	char *p;
-	int j, c, k;
-	char *sp;
-
-	sp = lp;
-	switch(o->type){
-	case Category:
-		if (o->parent == root) {
-			j = snprint(lp, ln, "category: ");
-			lp += j; ln -= j;
-		} else {
-			for (k = Ntoken; k < ntoken; k++)
-				if (catseteq(&o->categories,& tokenlist[k].categories)) {
-					j = snprint(lp, ln, "%s: ", tokenlist[k].name);
-					lp += j; ln -= j;
-//					break;
-				}
-		}
-		p = o->value;
-		if (p == 0) p = o->key;
-		while ((c=*p++) && c != '\n' && ln > 0) {
-			if(c == '~')
-				continue;
-			*lp++ = c;
-			ln--;
-		}
-		break;
-	case Track:
-	case Part:
-	case Recording:
-	case Root:
-	case Search:
-	case Work:
-		j = snprint(lp, ln, "%s: ", tokenlist[o->type].name);
-		lp += j; ln -= j;
-		if (o->num) {
-			j = snprint(lp, ln, "%2d. ", o->num);
-			lp += j; ln -= j;
-		}
-		p = o->value;
-		if (p == 0) p = o->key;
-		while ((c = *p++) && c != '\n' && ln > 0) {
-			if(c == '~')
-				continue;
-			*lp++ =  c;
-			ln--;
-		}
-	default:
-		break;
-	}
-	if(ln)
-		*lp = '\0';
-	return lp - sp;
-}
-
-#ifdef UNDEF
-
-void
-printtree(Object *o, int ind) {
-	char *p;
-	char buf[2048];
-	int i;
-
-	sprintf(buf, "%s {\n", tokenlist[o->type].name);
-	indent(ind, buf);
-	ind++;
-	if ((p = o->value)) {
-		sprintf(buf, "%s\n", p);
-		indent(ind, buf);
-	}
-	for (i = 0; i < o->nchildren; i++)
-		printtree(o->children[i], ind);
-	indent(--ind, "}\n");
-}
-
-void
-mapdump(Object *o, int depth, char *inheritance) {
-	Object *oo;
-	char *data;
-	int n, l;
-
-	if (o == root) {
-	    depth = 0;
-	    inheritance = "";
-	    data = NULL;
-	} else {
-	    if (o->value) {
-		l = nlines(o->value);
-	        n = strlen(inheritance) +
-		    l * (strlen(tokenlist[o->type].name) + 2) +
-		    strlen(o->value) + 2;
-		data = mymalloc(NULL, n);
-		strcpy(data, inheritance);
-		p = data + strlen(inheritance);
-		q = o->value;
-		while (*q) {
-		    p += sprintf(p, "%s:\t", tokenlist[o->type].name);
-		    do {
-			*p++ = *q;
-		    while (*q++ != '\n');
-		}
-		if (p[-1] != '\n') *p++ = '\n';
-		*p = 0;
-		inheritance = data;
-	    }
-	    indent(depth, inheritance);
-	}
-	c = 0;
-}
-
-#endif
--- a/sys/src/games/music/jukefs/print.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,6 +0,0 @@
-extern int		fflag;
-
-void printtree(Object *, int);
-int parentage(char *, int, Object *);
-int miniparentage(char *, int, Object *);
-int indent(char *, int, int n, char *buf);
--- a/sys/src/games/music/jukefs/search.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,44 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-#include <thread.h>
-#include "object.h"
-#include "parse.h"
-#include "search.h"
-
-Object *
-search(Object *rt, Object *parent, Reprog *preg) {
-	/* Create a `search object', a subtree of rt containing
-	 * only objects with s in their value of key fields plus
-	 * their parentage.
-	 *
-	 * Algorithm: depth-first traversal of rt.  On the way down,
-	 * copy rt to nr (new root), on the way back up, delete
-	 * subtrees without match.
-	 *
-	 * returns null when there are no matches in rt's subtree
-	 */
-	Object *o, *nr;
-	char *s;
-	int i;
-	int yes = 0;
-
-	nr = newobject(rt->type, parent);
-	nr->orig = rt->orig?rt->orig:rt;
-	nr->value = rt->value;
-	strncpy(nr->key, rt->key, KEYLEN);
-
-	if((((s = nr->value)) && regexec(preg, s, nil, 0) == 1)
-	|| (((s = nr->value)) && regexec(preg, s, nil, 0) == 1))
-		yes = 1;
-	for(i = 0; i < rt->nchildren; i++)
-		if((o = search((Object*)rt->children[i], nr, preg))){
-			yes = 1;
-			addchild(nr, o, "search");
-		}
-	if(yes == 0){
-		freeobject(nr, "s");
-		return nil;
-	}
-	return nr;
-}
--- a/sys/src/games/music/jukefs/search.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,5 +0,0 @@
-#include <regexp.h>
-
-extern Object *sobj;
-
-Object *search(Object *rt, Object *parent, Reprog *preg);
--- a/sys/src/games/music/jukefs/server.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,214 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <bio.h>
-#include <fcall.h>
-#include "object.h"
-#include "parse.h"
-#include "print.h"
-#include "catset.h"
-#include "../debug.h"
-
-char		*user, *mapname, *svrname;
-int		p[2];
-int		mfd[2];
-int		debug = 0; //DBGSERVER|DBGSTATE|DBGPICKLE|DBGPLAY;
-Biobuf		*f;
-char		*file;
-
-Object *root;
-
-Object **	otab;		// object table
-int		notab;	// no of entries used
-int		sotab;	// no of entries mallocated (invariant sotab >= notab)
-int		hotab;	// no of holes in otab;
-
-char usage[] = "Usage: %s [-f] [-l] [mapfile]\n";
-
-char *startdir;
-
-Object **catobjects;	/* for quickly finding category objects */
-int ncat = 0;
-
-void
-post(char *name, char *envname, int srvfd)
-{
-	int fd;
-	char buf[32];
-
-	fd = create(name, OWRITE, 0666);
-	if(fd < 0)
-		return;
-	sprint(buf, "%d",srvfd);
-	if(write(fd, buf, strlen(buf)) != strlen(buf))
-		sysfatal("srv write: %r");
-	close(fd);
-	putenv(envname, name);
-}
-
-int
-robusthandler(void*, char *s)
-{
-	if (debug) fprint(2, "inthandler: %s\n", s);
-	return (s && (strstr(s, "interrupted") || strstr(s, "hangup")));
-}
-
-long
-robustread(int fd, void *buf, long sz)
-{
-	long r;
-	char err[32];
-
-	do {
-		r = read(fd , buf, sz);
-		if (r < 0)
-			rerrstr(err, sizeof(err));
-	} while (r < 0 && robusthandler(nil, err));
-	return r;
-}
-
-void
-delobject(Object *o)
-{
-	/* Free an object and all its descendants */
-	Object *oo;
-	int i;
-
-	for (i = 0; i < o->nchildren; i++){
-		oo = o->children[i];
-		if (oo->parent == o)
-			delobject(oo);
-	}
-	freeobject(o, "r");
-}
-
-void
-threadmain(int argc, char *argv[]) {
-	char *q;
-	char *srvname;
-	char *mntpt;
-	int list;
-
-	mntpt = "/mnt";
-	user = strdup(getuser());
-	srvname = nil;
-	list = 0;
-
-	ARGBEGIN{
-	case 'l':
-		list = 1;
-		break;
-	case 'm':
-		mntpt = ARGF();
-		break;
-	case 'd':
-		debug = strtoul(ARGF(), nil, 0);
-		break;
-	case 's':
-		srvname = ARGF();
-		break;
-	case 'f':
-		fflag = 1;
-		break;
-	default:
-		fprint(2, usage, argv0);
-		exits("usage");
-	}ARGEND
-
-	switch (argc) {
-	default:
-		fprint(2, usage, argv0);
-		exits("usage");
-	case 0:
-		mapname = DEFAULTMAP;
-		break;
-	case 1:
-		mapname = argv[0];
-		break;
-	}
-
-	quotefmtinstall();
-
-	if((f = Bopen(mapname, OREAD)) == nil)
-		sysfatal("%s: %r", mapname);
-	free(file);
-	file = strdup(mapname);
-	free(startdir);
-	startdir = strdup(mapname);
-	if ((q = strrchr(startdir, '/')))
-		*q = '\0';
-	else
-		startdir[0] = '\0';
-	inittokenlist();
-	getobject(Root, nil);
-	Bterm(f);
-	f = nil;
-	root->parent = root;
-
-	if(list){
-		listfiles(root);
-		threadexits(nil);
-	}
-
-	if(pipe(p) < 0)
-		sysfatal("pipe failed: %r");
-	mfd[0] = p[0];
-	mfd[1] = p[0];
-
-	threadnotify(robusthandler, 1);
-	user = strdup(getuser());
-
-	if(debug)
-		fmtinstall('F', fcallfmt);
-
-	procrfork(io, nil, 8192, RFFDG);	//RFNOTEG?
-
-	close(p[0]);	/* don't deadlock if child fails */
-
-	if(srvname){
-		srvname = smprint("/srv/jukefs.%s", srvname);
-		remove(srvname);
-		post(srvname, "jukefs", p[1]);
-	}
-	if(mount(p[1], -1, mntpt, MBEFORE, "") < 0)
-		sysfatal("mount failed: %r");
-	threadexits(nil);
-}
-
-void
-reread(void)
-{
-	int i;
-	extern int catnr;
-	char *q;
-
-	assert(f == nil);
-	if((f = Bopen(mapname, OREAD)) == nil)
-		fprint(2, "reread: %s: %r\n", mapname);
-	freetree(root);
-	root = nil;
-	for(i = 0; i< ntoken; i++){
-		free(tokenlist[i].name);
-		catsetfree(&tokenlist[i].categories);
-	}
-	catnr = 0;
-	free(tokenlist);
-	free(catobjects);
-	catobjects = nil;
-	ncat = 0;
-	tokenlist = nil;
-	ntoken = Ntoken;
-	inittokenlist();
-	free(file);
-	file = strdup(mapname);
-	free(startdir);
-	startdir = strdup(mapname);
-	if ((q = strrchr(startdir, '/')))
-		*q = '\0';
-	else
-		startdir[0] = '\0';
-	getobject(Root, nil);
-	root->parent = root;
-	Bterm(f);
-	f = nil;
-}
--- a/sys/src/games/music/mkfile	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,51 +0,0 @@
-dirs = playlistfs jukefs jukebox
-
-# DEFAULTMAP = /lib/audio/map
-ICONPATH = /lib/audio/icon
-
-ICONS = \
-	next.bit\
-	pause.bit\
-	play.bit\
-	prev.bit\
-	question.bit\
-	root.bit\
-	skull.bit\
-	stop.bit\
-	trash.bit\
-
-ICONFILES = ${ICONS:%.bit=icon/%.bit}
-
-default:V: all
-
-all dep clean nuke:V:
-	for (i in $dirs) @ {
-		echo $i
-		cd $i
-		mk $MKFLAGS $target
-	}
-
-rcinstall:V:	juke.rc
-	cp juke.rc /rc/bin/juke
-	chmod +x /rc/bin/juke
-
-$ICONPATH:
-	mkdir $ICONPATH
-
-iconinstall:V:	$ICONFILES $ICONPATH
-	for (i in $ICONS)
-		cp $ICONFILES $ICONPATH
-
-install:V:
-	for (i in $dirs) @ {
-		echo $i
-		cd $i
-		mk $MKFLAGS $target
-	}
-	mk rcinstall
-	mk iconinstall
-
-installall:V:
-	for(objtype in $CPUS)
-		mk $MKFLAGS install
-	mk rcinstall
--- a/sys/src/games/music/mkinc	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,9 +0,0 @@
-base = ..
-CFLAGS=$CFLAGS $DEFINES
-
-all:
-
-dep:Q:
-	mkdep $INCLUDES $CFILES > mk.dep
-
-< mk.dep
--- a/sys/src/games/music/playlistfs/boilerplate.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,100 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <fcall.h>
-#include "playlist.h"
-
-static Channel *reqs;
-
-Req*
-reqalloc(void)
-{
-	Req *r;
-
-	if(reqs == nil)
-		reqs = chancreate(sizeof(Req*), 256);
-	if(r = nbrecvp(reqs))
-		return r;
-	r = malloc(sizeof(Req));
-	return r;
-}
-
-void
-reqfree(Req *r)
-{
-	if(!nbsendp(reqs, r))
-		free(r);
-}
-
-Wmsg
-waitmsg(Worker *w, Channel *q)
-{
-	Wmsg m;
-
-	sendp(q, w);
-	recv(w->eventc, &m);
-	return m;
-}
-
-int
-sendmsg(Channel *q, Wmsg *m)
-{
-	Worker *w;
-
-	while(w = nbrecvp(q)){
-		/* Test for markerdom (see bcastmsg) */
-		if(w->eventc){
-			send(w->eventc, m);
-			return 1;
-		}
-		sendp(q, w);	/* put back */
-	}
-	return 0;
-}
-
-void
-bcastmsg(Channel *q, Wmsg *m)
-{
-	Worker *w, marker;
-	void *a;
-
-	a = m->arg;
-	/*
-	 * Use a marker to mark the end of the queue.
-	 * This prevents workers from getting the
-	 * broadcast and putting themselves back on the
-	 * queue before the broadcast has finished
-	 */
-	marker.eventc = nil;	/* Only markers have eventc == nil */
-	sendp(q, &marker);
-	while((w = recvp(q)) != &marker){
-		if(w->eventc == nil){
-			/* somebody else's marker, put it back */
-			sendp(q, w);
-		}else{
-			if(a) m->arg = strdup(a);
-			send(w->eventc, m);
-		}
-	}
-	free(a);
-	m->arg = nil;
-}
-
-void
-readbuf(Req *r, void *s, long n)
-{
-	r->ofcall.count = r->ifcall.count;
-	if(r->ifcall.offset >= n){
-		r->ofcall.count = 0;
-		return;
-	}
-	if(r->ifcall.offset+r->ofcall.count > n)
-		r->ofcall.count = n - r->ifcall.offset;
-	memmove(r->ofcall.data, (char*)s+r->ifcall.offset, r->ofcall.count);
-}
-
-void
-readstr(Req *r, char *s)
-{
-	readbuf(r, s, strlen(s));
-}
--- a/sys/src/games/music/playlistfs/fs.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,882 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <fcall.h>
-#include "pool.h"
-#include "playlist.h"
-
-typedef struct Wmsg Wmsg;
-
-enum {
-	Busy =	0x01,
-	Open =	0x02,
-	Trunc =	0x04,
-	Eof =	0x08,
-};
-
-File files[] = {
-[Qdir] =	{.dir = {0,0,{Qdir, 0,QTDIR},		0555|DMDIR,	0,0,0,	"."}},
-[Qplayctl] =	{.dir = {0,0,{Qplayctl, 0,QTFILE},	0666,		0,0,0,	"playctl"}},
-[Qplaylist] =	{.dir = {0,0,{Qplaylist, 0,QTFILE},	0666|DMAPPEND,	0,0,0,	"playlist"}},
-[Qplayvol] =	{.dir = {0,0,{Qplayvol, 0,QTFILE},	0666,		0,0,0,	"playvol"}},
-[Qplaystat] =	{.dir = {0,0,{Qplaystat, 0,QTFILE},	0444,		0,0,0,	"playstat"}},
-};
-
-Channel		*reqs;
-Channel		*workers;
-Channel		*volumechan;
-Channel		*playchan;
-Channel		*playlistreq;
-Playlist	playlist;
-int		volume[8];
-
-char *statetxt[] = {
-	[Nostate] =	"panic!",
-	[Error] =	"error",
-	[Stop] =	"stop",
-	[Pause] =	"pause",
-	[Play] =	"play",
-	[Resume] =	"resume",
-	[Skip] =	"skip",
-	nil
-};
-
-// low-order bits: position in play list, high-order: play state:
-Pmsg	playstate = {Stop, 0};
-
-char	*rflush(Worker*), *rauth(Worker*),
-	*rattach(Worker*), *rwalk(Worker*),
-	*ropen(Worker*), *rcreate(Worker*),
-	*rread(Worker*), *rwrite(Worker*), *rclunk(Worker*),
-	*rremove(Worker*), *rstat(Worker*), *rwstat(Worker*),
-	*rversion(Worker*);
-
-char 	*(*fcalls[])(Worker*) = {
-	[Tflush]	rflush,
-	[Tversion]	rversion,
-	[Tauth]		rauth,
-	[Tattach]	rattach,
-	[Twalk]		rwalk,
-	[Topen]		ropen,
-	[Tcreate]	rcreate,
-	[Tread]		rread,
-	[Twrite]	rwrite,
-	[Tclunk]	rclunk,
-	[Tremove]	rremove,
-	[Tstat]		rstat,
-	[Twstat]	rwstat,
-};
-
-int	messagesize = Messagesize;
-Fid	*fids;
-
-
-char	Eperm[] =	"permission denied";
-char	Enotdir[] =	"not a directory";
-char	Enoauth[] =	"authentication not required";
-char	Enotexist[] =	"file does not exist";
-char	Einuse[] =	"file in use";
-char	Eexist[] =	"file exists";
-char	Enotowner[] =	"not owner";
-char	Eisopen[] = 	"file already open for I/O";
-char	Excl[] = 	"exclusive use file already open";
-char	Ename[] = 	"illegal name";
-char	Ebadctl[] =	"unknown control message";
-char	Epast[] =	"reading past eof";
-
-Fid	*oldfid(int);
-Fid	*newfid(int);
-void	volumeupdater(void*);
-void	playupdater(void*);
-
-char *playerror;
-
-static int
-lookup(char *cmd, char *list[])
-{
-	int i;
-
-	for (i = 0; list[i] != nil; i++)
-		if (strcmp(cmd, list[i]) == 0)
-			return i;
-	return -1;
-}
-
-char*
-rversion(Worker *w)
-{
-	Req *r;
-	Fid *f;
-
-	r = w->r;
-	if(r->ifcall.msize < 256)
-		return "max messagesize too small";
-	if(r->ifcall.msize < messagesize)
-		messagesize = r->ifcall.msize;
-	r->ofcall.msize = messagesize;
-	if(strncmp(r->ifcall.version, "9P2000", 6) != 0)
-		return "unknown 9P version";
-	else
-		r->ofcall.version = "9P2000";
-	for(f = fids; f; f = f->next)
-		if(f->flags & Busy)
-			f->flags &= ~(Open|Busy);
-	return nil;
-}
-
-char*
-rauth(Worker*)
-{
-	return Enoauth;
-}
-
-char*
-rflush(Worker *w)
-{
-	Wmsg m;
-	int i;
-	Req *r;
-
-	r = w->r;
-	m.cmd = Flush;
-	m.off = r->ifcall.oldtag;
-	m.arg = nil;
-	for(i = 1; i < nelem(files); i++)
-		bcastmsg(files[i].workers, &m);
-	if (debug & DbgWorker)
-		fprint(2, "flush done on tag %d\n", r->ifcall.oldtag);
-	return 0;
-}
-
-char*
-rattach(Worker *w)
-{
-	Fid *f;
-	Req *r;
-
-	r = w->r;
-	r->fid = newfid(r->ifcall.fid);
-	f = r->fid;
-	f->flags |= Busy;
-	f->file = &files[Qdir];
-	r->ofcall.qid = f->file->dir.qid;
-	if(!aflag && strcmp(r->ifcall.uname, user) != 0)
-		return Eperm;
-	return 0;
-}
-
-static Fid*
-doclone(Fid *f, int nfid)
-{
-	Fid *nf;
-
-	nf = newfid(nfid);
-	if(nf->flags & Busy)
-		return nil;
-	nf->flags |= Busy;
-	nf->flags &= ~(Open);
-	nf->file = f->file;
-	return nf;
-}
-
-char*
-dowalk(Fid *f, char *name)
-{
-	int t;
-
-	if (strcmp(name, ".") == 0)
-		return nil;
-	if (strcmp(name, "..") == 0){
-		f->file = &files[Qdir];
-		return nil;
-	}
-	if(f->file != &files[Qdir])
-		return Enotexist;
-	for (t = 1; t < Nqid; t++){
-		if(strcmp(name, files[t].dir.name) == 0){
-			f->file = &files[t];
-			return nil;
-		}
-	}
-	return Enotexist;
-}
-
-char*
-rwalk(Worker *w)
-{
-	Fid *f, *nf;
-	char *rv;
-	int i;
-	File *savefile;
-	Req *r;
-
-	r = w->r;
-	r->fid = oldfid(r->ifcall.fid);
-	if((f = r->fid) == nil)
-		return Enotexist;
-	if(f->flags & Open)
-		return Eisopen;
-
-	r->ofcall.nwqid = 0;
-	nf = nil;
-	savefile = f->file;
-	/* clone if requested */
-	if(r->ifcall.newfid != r->ifcall.fid){
-		nf = doclone(f, r->ifcall.newfid);
-		if(nf == nil)
-			return "new fid in use";
-		f = nf;
-	}
-
-	/* if it's just a clone, return */
-	if(r->ifcall.nwname == 0 && nf != nil)
-		return nil;
-
-	/* walk each element */
-	rv = nil;
-	for(i = 0; i < r->ifcall.nwname; i++){
-		rv = dowalk(f, r->ifcall.wname[i]);
-		if(rv != nil){
-			if(nf != nil)	
-				nf->flags &= ~(Open|Busy);
-			else
-				f->file = savefile;
-			break;
-		}
-		r->ofcall.wqid[i] = f->file->dir.qid;
-	}
-	r->ofcall.nwqid = i;
-
-	/* we only error out if no walk  */
-	if(i > 0)
-		rv = nil;
-
-	return rv;
-}
-
-char *
-ropen(Worker *w)
-{
-	Fid *f, *ff;
-	Wmsg m;
-	Req *r;
-
-	r = w->r;
-	r->fid = oldfid(r->ifcall.fid);
-	if((f = r->fid) == nil)
-		return Enotexist;
-	if(f->flags & Open)
-		return Eisopen;
-
-	if(r->ifcall.mode != OREAD)
-		if((f->file->dir.mode & 0x2) == 0)
-			return Eperm;
-	if((r->ifcall.mode & OTRUNC) && f->file == &files[Qplaylist]){
-		playlist.nlines = 0;
-		playlist.ndata = 0;
-		free(playlist.lines);
-		free(playlist.data);
-		playlist.lines = nil;
-		playlist.data = nil;
-		f->file->dir.length = 0;
-		f->file->dir.qid.vers++;
-		/* Mark all fids for this file `Trunc'ed */
-		for(ff = fids; ff; ff = ff->next)
-			if(ff->file == &files[Qplaylist] && (ff->flags & Open))
-				ff->flags |= Trunc;
-		m.cmd = Check;
-		m.off = 0;
-		m.arg = nil;
-		bcastmsg(f->file->workers, &m);
-	}
-	r->ofcall.iounit = 0;
-	r->ofcall.qid = f->file->dir.qid;
-	f->flags |= Open;
-	return nil;
-}
-
-char *
-rcreate(Worker*)
-{
-	return Eperm;
-}
-
-int
-readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
-{
-	int i, m, n;
-	long pos;
-
-	n = 0;
-	pos = 0;
-	for (i = 1; i < Nqid; i++){
-		m = convD2M(&files[i].dir, &buf[n], blen-n);
-		if(off <= pos){
-			if(m <= BIT16SZ || m > cnt)
-				break;
-			n += m;
-			cnt -= m;
-		}
-		pos += m;
-	}
-	return n;
-}
-
-char*
-rread(Worker *w)
-{
-	Fid *f;
-	Req *r;
-	long off, cnt;
-	int n, i;
-	Wmsg m;
-	char *p;
-
-	r = w->r;
-	r->fid = oldfid(r->ifcall.fid);
-	if((f = r->fid) == nil)
-		return Enotexist;
-	r->ofcall.count = 0;
-	off = r->ifcall.offset;
-	cnt = r->ifcall.count;
-
-	if(cnt > messagesize - IOHDRSZ)
-		cnt = messagesize - IOHDRSZ;
-
-	if(f->file == &files[Qdir]){
-		n = readtopdir(f, r->indata, off, cnt, messagesize - IOHDRSZ);
-		r->ofcall.count = n;
-		return nil;
-	}
-
-	if(f->file == files + Qplaystat){
-		p = getplaystat(r->ofcall.data, r->ofcall.data + sizeof r->indata);
-		readbuf(r, r->ofcall.data, p - r->ofcall.data);
-		return nil;
-	}
-
-	m.cmd = 0;
-	while(f->vers == f->file->dir.qid.vers && (f->flags & Eof)){
-		/* Wait until file state changes (f->file->dir.qid.vers is incremented) */
-		m = waitmsg(w, f->file->workers);
-		if(m.cmd == Flush && m.off == r->ifcall.tag)
-			return (char*)~0;	/* no answer needed */
-		assert(m.cmd != Work);
-		yield();
-	}
-	if(f->file == files + Qplaylist){
-		f->flags &= ~Eof;
-		if((f->flags & Trunc) && r->ifcall.offset != 0){
-			f->flags &= ~Trunc;
-			return Epast;
-		}
-		f->flags &= ~Trunc;
-		if(r->ifcall.offset < playlist.ndata)
-			readbuf(r, playlist.data, playlist.ndata);
-		else if(r->ifcall.offset == playlist.ndata){
-			r->ofcall.count = 0;
-			/* Arrange for this fid to wait next time: */
-			f->vers = f->file->dir.qid.vers;
-			f->flags |= Eof;
-		}else{
-			/* Beyond eof, bad seek? */
-			return Epast;
-		}
-	}else if(f->file == files + Qplayctl){
-		f->flags &= ~Eof;
-		if(m.cmd == Error){
-			snprint(r->ofcall.data, sizeof r->indata, "%s %ud %q",
-				statetxt[m.cmd], m.off, m.arg);
-			free(m.arg);
-		}else if(f->vers == f->file->dir.qid.vers){
-			r->ofcall.count = 0;
-			/* Arrange for this fid to wait next time: */
-			f->flags |= Eof;
-			return nil;
-		}else{
-			snprint(r->ofcall.data, sizeof r->indata, "%s %ud",
-				statetxt[playstate.cmd], playstate.off);
-			f->vers = f->file->dir.qid.vers;
-		}
-		r->ofcall.count = strlen(r->ofcall.data);
-		if(r->ofcall.count > r->ifcall.count)
-			r->ofcall.count = r->ifcall.count;
-	}else if(f->file == files + Qplayvol){
-		f->flags &= ~Eof;
-		if(f->vers == f->file->dir.qid.vers){
-			r->ofcall.count = 0;
-			/* Arrange for this fid to wait next time: */
-			f->flags |= Eof;
-		}else{
-			p = seprint(r->ofcall.data, r->ofcall.data + sizeof r->indata, "volume	'");
-			for(i = 0; i < nelem(volume); i++){
-				if(volume[i] == Undef)
-					break;
-				p = seprint(p, r->ofcall.data + sizeof r->indata, "%d ", volume[i]);
-			}
-			p = seprint(p, r->ofcall.data + sizeof r->indata, "'");
-			r->ofcall.count = p - r->ofcall.data;
-			if(r->ofcall.count > r->ifcall.count)
-				r->ofcall.count = r->ifcall.count;
-			f->vers = f->file->dir.qid.vers;
-		}
-	}else
-		abort();
-	return nil;
-}
-
-char*
-rwrite(Worker *w)
-{
-	long cnt, i, nf, cmd;
-	Pmsg newstate;
-	char *fields[3], *p, *q;
-	Wmsg m;
-	Fid *f;
-	Req *r;
-
-	r = w->r;
-	r->fid = oldfid(r->ifcall.fid);
-	if((f = r->fid) == nil)
-		return Enotexist;
-	r->ofcall.count = 0;
-	cnt = r->ifcall.count;
-
-	if(cnt > messagesize - IOHDRSZ)
-		cnt = messagesize - IOHDRSZ;
-
-	if(f->file == &files[Qplayctl]){
-		r->ifcall.data[cnt] = '\0';
-		if (debug & DbgPlayer)
-			fprint(2, "rwrite playctl: %s\n", r->ifcall.data);
-		nf = tokenize(r->ifcall.data, fields, 4);
-		if (nf == 0){
-			r->ofcall.count = r->ifcall.count;
-			return nil;
-		}
-		if (nf == 2)
-			i = strtol(fields[1], nil, 0);
-		else
-			i = playstate.off;
-		newstate = playstate;
-		if ((cmd = lookup(fields[0], statetxt)) < 0)
-			return  Ebadctl;
-		switch(cmd){
-		case Play:
-			newstate.cmd = cmd;
-			newstate.off = i;
-			break;
-		case Pause:
-			if (playstate.cmd != Play)
-				break;
-			// fall through
-		case Stop:
-			newstate.cmd = cmd;
-			newstate.off = playstate.off;
-			break;
-		case Resume:
-			if(playstate.cmd == Stop)
-				break;
-			newstate.cmd = Resume;
-			newstate.off = playstate.off;
-			break;
-		case Skip:
-			if (nf == 2)
-				i += playstate.off;
-			else
-				i = playstate.off +1;
-			if(i < 0)
-				i = 0;
-			else if (i >= playlist.nlines)
-				i = playlist.nlines - 1;
-			newstate.cmd = Play;
-			newstate.off = i;
-		}
-		if (newstate.off >= playlist.nlines){
-			newstate.cmd = Stop;
-			newstate.off = playlist.nlines;
-		}
-		if (debug & DbgPlayer)
-			fprint(2, "new state %s-%ud\n",
-				statetxt[newstate.cmd], newstate.off);
-		if (newstate.m != playstate.m)
-			sendul(playc, newstate.m);
-		f->file->dir.qid.vers++;
-	} else if(f->file == &files[Qplayvol]){
-		char *subfields[nelem(volume)];
-		int v[nelem(volume)];
-
-		r->ifcall.data[cnt] = '\0';
-		if (debug & DbgPlayer)
-			fprint(2, "rwrite playvol: %s\n", r->ifcall.data);
-		nf = tokenize(r->ifcall.data, fields, 4);
-		if (nf == 0){
-			r->ofcall.count = r->ifcall.count;
-			return nil;
-		}
-		if (nf != 2 || strcmp(fields[0], "volume") != 0)
-			return Ebadctl;
-		if (debug & DbgPlayer)
-			fprint(2, "new volume '");
-		nf = tokenize(fields[1], subfields, nelem(subfields));
-		if (nf <= 0 || nf > nelem(volume))
-			return "volume";
-		for (i = 0; i < nf; i++){
-			v[i] = strtol(subfields[i], nil, 0);
-			if (debug & DbgPlayer)
-				fprint(2, " %d", v[i]);
-		}
-		if (debug & DbgPlayer)
-			fprint(2, "'\n");
-		while (i < nelem(volume))
-			v[i++] = Undef;
-		volumeset(v);
-		r->ofcall.count = r->ifcall.count;
-		return nil;
-	} else if(f->file == &files[Qplaylist]){
-		if (debug & DbgPlayer){
-			fprint(2, "rwrite playlist: `");
-			write(2, r->ifcall.data, cnt);
-			fprint(2, "'\n");
-		}
-		playlist.data = realloc(playlist.data, playlist.ndata + cnt + 2);
-		if (playlist.data == 0)
-			sysfatal("realloc: %r");
-		memmove(playlist.data + playlist.ndata, r->ifcall.data, cnt);
-		if (playlist.data[playlist.ndata + cnt-1] != '\n')
-			playlist.data[playlist.ndata + cnt++] = '\n';
-		playlist.data[playlist.ndata + cnt] = '\0';
-		p = playlist.data + playlist.ndata;
-		while (*p){
-			playlist.lines = realloc(playlist.lines, (playlist.nlines+1)*sizeof(playlist.lines[0]));
-			if(playlist.lines == nil)
-				sysfatal("realloc: %r");
-			playlist.lines[playlist.nlines] = playlist.ndata;
-			q = strchr(p, '\n');
-			if (q == nil)
-				break;
-			if(debug & DbgPlayer)
-				fprint(2, "[%lud]: ", playlist.nlines);
-			playlist.nlines++;
-			q++;
-			if(debug & DbgPlayer)
-				write(2, p, q-p);
-			playlist.ndata += q - p;
-			p = q;
-		}
-		f->file->dir.length = playlist.ndata;
-		f->file->dir.qid.vers++;
-	}else
-		return Eperm;
-	r->ofcall.count = r->ifcall.count;
-	m.cmd = Check;
-	m.off = 0;
-	m.arg = nil;
-	bcastmsg(f->file->workers, &m);
-	return nil;
-}
-
-char *
-rclunk(Worker *w)
-{
-	Fid *f;
-
-	f = oldfid(w->r->ifcall.fid);
-	if(f == nil)
-		return Enotexist;
-	f->flags &= ~(Open|Busy);
-	return 0;
-}
-
-char *
-rremove(Worker*)
-{
-	return Eperm;
-}
-
-char *
-rstat(Worker *w)
-{
-	Req *r;
-
-	r = w->r;
-	r->fid = oldfid(r->ifcall.fid);
-	if(r->fid == nil)
-		return Enotexist;
-	r->ofcall.nstat = convD2M(&r->fid->file->dir, r->indata, messagesize - IOHDRSZ);
-	r->ofcall.stat = r->indata;
-	return 0;
-}
-
-char *
-rwstat(Worker*)
-{
-	return Eperm;
-}
-
-Fid *
-oldfid(int fid)
-{
-	Fid *f;
-
-	for(f = fids; f; f = f->next)
-		if(f->fid == fid)
-			return f;
-	return nil;
-}
-
-Fid *
-newfid(int fid)
-{
-	Fid *f, *ff;
-
-	ff = nil;
-	for(f = fids; f; f = f->next)
-		if(f->fid == fid){
-			return f;
-		}else if(ff == nil && (f->flags & Busy) == 0)
-			ff = f;
-	if(ff == nil){
-		ff = mallocz(sizeof *ff, 1);
-		if (ff == nil)
-			sysfatal("malloc: %r");
-		ff->next = fids;
-		ff->readers = 0;
-		fids = ff;
-	}
-	ff->fid = fid;
-	ff->file = nil;
-	ff->vers = ~0;
-	return ff;
-}
-
-void
-work(Worker *w)
-{
-	Req *r;
-	char *err;
-	int n;
-
-	r = w->r;
-	r->ofcall.data = (char*)r->indata;
-	if(!fcalls[r->ifcall.type])
-		err = "bad fcall type";
-	else
-		err = (*fcalls[r->ifcall.type])(w);
-	if(err != (char*)~0){	/* ~0 indicates Flush received */
-		if(err){
-			r->ofcall.type = Rerror;
-			r->ofcall.ename = err;
-		}else{
-			r->ofcall.type = r->ifcall.type + 1;
-			r->ofcall.fid = r->ifcall.fid;
-		}
-		r->ofcall.tag = r->ifcall.tag;
-		if(debug & DbgFs)
-			fprint(2, "io:->%F\n", &r->ofcall);/**/
-		n = convS2M(&r->ofcall, r->outdata, messagesize);
-		if(write(srvfd[0], r->outdata, n) != n)
-			sysfatal("mount write");
-	}
-	reqfree(r);
-	w->r = nil;
-}
-
-void
-worker(void *arg)
-{
-	Worker *w;
-	Wmsg m;
-
-	w = arg;
-	recv(w->eventc, &m);
-	for(;;){
-		assert(m.cmd == Work);
-		w->r = m.arg;
-		if(debug & DbgWorker)
-			fprint(2, "worker 0x%p:<-%F\n", w, &w->r->ifcall);
-		work(w);
-		if(debug & DbgWorker)
-			fprint(2, "worker 0x%p wait for next\n", w);
-		m = waitmsg(w, workers);
-	}
-}
-
-void
-allocwork(Req *r)
-{
-	Worker *w;
-	Wmsg m;
-
-	m.cmd = Work;
-	m.off = 0;
-	m.arg = r;
-	if(sendmsg(workers, &m))
-		return;
-	/* No worker ready to accept request, allocate one */
-	w = malloc(sizeof(Worker));
-	w->eventc = chancreate(sizeof(Wmsg), 1);
-	if(debug & DbgWorker)
-		fprint(2, "new worker 0x%p\n", w);/**/
-	threadcreate(worker, w, 4096);
-	send(w->eventc, &m);
-}
-
-void
-srvio(void *arg)
-{
-	int n;
-	Req *r;
-	Channel *dispatchc;
-
-	threadsetname("file server IO");
-	dispatchc = arg;
-
-	r = reqalloc();
-	while((n = read9pmsg(srvfd[0], r->indata, messagesize)) != 0){
-		if(n < 0){
-			char e[32];
-			rerrstr(e, sizeof e);
-			if (strcmp(e, "interrupted") == 0){
-				if (debug & DbgFs) fprint(2, "read9pmsg interrupted\n");
-				continue;
-			}
-			sysfatal("srvio: read: %s", e);
-		}
-		if(convM2S(r->indata, n, &r->ifcall) != n)
-			sysfatal("srvio: convM2S: %r");
-
-		if(debug & DbgFs)
-			fprint(2, "io:<-%F\n", &r->ifcall);
-		sendp(dispatchc, r);
-		r = reqalloc();
-	}
-	threadexitsall(nil);
-}
-
-char *
-getplaylist(int n)
-{
-	Wmsg m;
-
-	m.cmd = Preq;
-	m.off = n;
-	m.arg = nil;
-	send(playlistreq, &m);
-	recv(playlistreq, &m);
-	if(m.cmd == Error)
-		return nil;
-	assert(m.cmd == Prep);
-	assert(m.arg);
-	return m.arg;
-}
-
-void
-playlistsrv(void*)
-{
-	Wmsg m;
-	char *p, *q, *r;
-	char *fields[2];
-	int n;
-	/* Runs in the srv proc */
-
-	threadsetname("playlistsrv");
-	while(recv(playlistreq, &m)){
-		assert(m.cmd == Preq);
-		m.cmd = Error;
-		if(m.off < playlist.nlines){
-			p = playlist.data + playlist.lines[m.off];
-			q = strchr(p, '\n');
-			if (q == nil)
-				sysfatal("playlistsrv: no newline character found");
-			n = q-p;
-			r = malloc(n+1);
-			memmove(r, p, n);
-			r[n] = 0;
-			tokenize(r, fields, nelem(fields));
-			assert(fields[0] == r);
-			m.cmd = Prep;
-			m.arg = r;
-		}
-		send(playlistreq, &m);
-	}
-}
-
-void
-srv(void*)
-{
-	Req *r;
-	Channel *dispatchc;
-	/*
-	 * This is the proc with all the action.
-	 * When a file request comes in, it is dispatched to this proc
-	 * for processing.  Two extra threads field changes in play state
-	 * and volume state.
-	 * By keeping all the action in this proc, we won't need locks
-	 */
-
-	threadsetname("srv");
-	close(srvfd[1]);
-
-	dispatchc = chancreate(sizeof(Req*), 1);
-	procrfork(srvio, dispatchc, 4096, RFFDG);
-
-	threadcreate(volumeupdater, nil, 4096);
-	threadcreate(playupdater, nil, 4096);
-	threadcreate(playlistsrv, nil, 4096);
-
-	while(r = recvp(dispatchc))
-		allocwork(r);
-		
-}
-
-void
-playupdater(void*)
-{
-	Wmsg m;
-	/* This is a thread in the srv proc */
-
-	while(recv(playchan, &m)){
-		if(debug & DbgPlayer)
-			fprint(2, "playupdate: %s %d %s\n", statetxt[m.cmd], m.off, m.arg?m.arg:"");
-		if(playstate.m == m.m)
-			continue;
-		if(m.cmd == Stop && m.off == 0xffff)
-			m.off = playlist.nlines;
-		if(m.cmd != Error){
-			playstate.m = m.m;
-			m.cmd = Check;
-			assert(m.arg == nil);
-		}
-		files[Qplayctl].dir.qid.vers++;
-		bcastmsg(files[Qplayctl].workers, &m);
-	}
-}
-
-void
-volumeupdater(void*)
-{
-	Wmsg m;
-	int v[nelem(volume)];
-	/* This is a thread in the srv proc */
-
-	while(recv(volumechan, v)){
-		if(debug & DbgPlayer)
-			fprint(2, "volumeupdate: volume now %d %d %d %d\n", volume[0], volume[1], volume[2], volume[3]);
-		memmove(volume, v, sizeof(volume));
-		files[Qplayvol].dir.qid.vers++;
-		m.cmd = Check;
-		m.arg = nil;
-		bcastmsg(files[Qplayvol].workers, &m);
-	}
-}
-
-void
-playupdate(Pmsg p, char *s)
-{
-	Wmsg m;
-
-	m.m = p.m;
-	m.arg = s ? strdup(s) : nil;
-	send(playchan, &m);
-}
--- a/sys/src/games/music/playlistfs/main.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,94 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <fcall.h>
-#include "playlist.h"
-
-int	debug;
-char	*user;
-int	srvfd[2];
-int	aflag;
-
-void
-usage(void)
-{
-	sysfatal("usage: %s [-d bitmask] [-s] [-m mountpoint]", argv0);
-}
-
-void
-post(char *name, int sfd)
-{
-	int fd;
-	char buf[32];
-
-	fd = create(name, OWRITE, 0666);
-	if(fd < 0)
-		return;
-	sprint(buf, "%d", sfd);
-	if(write(fd, buf, strlen(buf)) != strlen(buf))
-		sysfatal("srv write: %r");
-	close(fd);
-}
-
-void
-threadmain(int argc, char *argv[])
-{
-	char *srvfile;
-	char *srvpost;
-	char *mntpt;
-	int i;
-
-	mntpt = "/mnt";
-	srvpost = nil;
-
-	rfork(RFNOTEG);
-
-	ARGBEGIN{
-	case 'a':
-		aflag = 1;
-		break;
-	case 'm':
-		mntpt = ARGF();
-		break;
-	case 'd':
-		debug = strtoul(ARGF(), nil, 0);
-		break;
-	case 's':
-		srvpost = ARGF();
-		break;
-	default:
-		usage();
-	}ARGEND
-
-	user = strdup(getuser());
-
-	quotefmtinstall();
-
-	if(debug)
-		fmtinstall('F', fcallfmt);
-
-	volumechan = chancreate(sizeof(volume), 1);
-	playchan = chancreate(sizeof(Wmsg), 1);
-	playlistreq = chancreate(sizeof(Wmsg), 0);	/* No storage! requires rendez-vous */
-	workers = chancreate(sizeof(Worker*), 256);
-	for(i = 1; i < Nqid; i++)
-		files[i].workers = chancreate(sizeof(Worker*), 256);
-
-	if(pipe(srvfd) < 0)
-		sysfatal("pipe failed: %r");
-	procrfork(srv, nil, 8192, RFFDG);
-	close(srvfd[0]);	/* don't deadlock if child fails */
-
-	procrfork(volumeproc, nil, 8192, RFFDG);
-	playinit();
-
-	if(srvpost){
-		srvfile = smprint("/srv/playlist.%s", srvpost);
-		remove(srvfile);
-		post(srvfile, srvfd[1]);
-		free(srvfile);
-	}
-	if(mount(srvfd[1], -1, mntpt, MBEFORE, "") < 0)
-		sysfatal("mount failed: %r");
-	threadexits(nil);
-}
--- a/sys/src/games/music/playlistfs/mk.dep	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,5 +0,0 @@
-main.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/fcall.h playlist.h
-fs.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/fcall.h /sys/include/pool.h playlist.h
-player.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/fcall.h /sys/include/pool.h playlist.h
-volume.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/fcall.h /sys/include/pool.h playlist.h
-boilerplate.$O: /$objtype/include/u.h /sys/include/libc.h /sys/include/thread.h /sys/include/fcall.h playlist.h
--- a/sys/src/games/music/playlistfs/mkfile	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,16 +0,0 @@
-</$objtype/mkfile
-<../mkinc
-
-TARG = playlistfs
-BIN = /$objtype/bin/games
-
-CFILES=\
-	main.c\
-	fs.c\
-	player.c\
-	volume.c\
-	boilerplate.c\
-
-OFILES = ${CFILES:%.c=%.$O}
-
-</sys/src/cmd/mkone
--- a/sys/src/games/music/playlistfs/player.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,373 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <fcall.h>
-#include "pool.h"
-#include "playlist.h"
-
-typedef struct Playfd Playfd;
-
-struct Playfd {
-	/* Describes a file to play for starting up pac4dec/mp3,... */
-	char	*filename;	/* mallocated */
-	int	fd;		/* filedesc to use */
-	int	cfd;		/* fildesc to close */
-};
-
-Channel *full, *empty, *playout, *spare;
-Channel	*playc, *pacc;
-
-ulong totbytes, totbuffers;
-
-static char curfile[8192];
-
-void
-decexec(void *a)
-{
-	char buf[256];
-	Playfd *pfd;
-	Pacbuf *pb;
-
-	threadsetname("decexec");
-	pfd = a;
-	close(pfd->cfd);	/* read fd */
-	if(pfd->fd != 1){
-		dup(pfd->fd, 1);
-		close(pfd->fd);
-	}
-	close(0); open("/dev/null", OREAD);
-	close(2); open("/dev/null", OWRITE);
-	strncpy(buf, pfd->filename, sizeof(buf)-1);
-	buf[sizeof(buf)-1] = 0;
-	free(pfd->filename);
-	free(pfd);
-	procexecl(nil, "/bin/play", "play", "-o", "/fd/1", buf, nil);
-	if((pb = nbrecvp(spare)) == nil)
-		pb = malloc(sizeof(Pacbuf));
-	pb->cmd = Error;
-	pb->off = 0;
-	pb->len = snprint(pb->data, sizeof(pb->data), "startplay: exec play failed");
-	sendp(full, pb);
-	threadexits("exec");
-}
-
-static int
-startplay(ushort n)
-{
-	int fd[2];
-	Playfd *pfd;
-	char *file;
-
-	file = getplaylist(n);
-	if(file == nil)
-		return Undef;
-	if (debug & DbgPlayer)
-		fprint(2, "startplay: file is `%s'\n", file);
-	if(pipe(fd) < 0)
-		sysfatal("pipe: %r");
-	pfd = malloc(sizeof(Playfd));
-	pfd->filename = file;	/* mallocated already */
-	pfd->fd = fd[1];
-	pfd->cfd = fd[0];
-	procrfork(decexec, pfd, 4096, RFFDG|RFENVG);
-	close(fd[1]);	/* write fd, for pac4dec */
-	return fd[0];	/* read fd */
-}
-
-static void
-rtsched(void)
-{
-	int fd;
-	char *ctl;
-
-	ctl = smprint("/proc/%ud/ctl", getpid());
-	if((fd = open(ctl, OWRITE)) < 0) 
-		sysfatal("%s: %r", ctl);
-	if(fprint(fd, "period 20ms") < 0)
-		sysfatal("%s: %r", ctl);
-	if(fprint(fd, "cost 100µs") < 0)
-		sysfatal("%s: %r", ctl);
-	if(fprint(fd, "sporadic") < 0)
-		sysfatal("%s: %r", ctl);
-	if(fprint(fd, "admit") < 0)
-		sysfatal("%s: %r", ctl);
-	close(fd);
-	free(ctl);
-}
-
-static void
-boost(void)
-{
-	int fd;
-	char *ctl;
-
-	ctl = smprint("/proc/%ud/ctl", getpid());
-	if((fd = open(ctl, OWRITE)) >= 0) {
-		fprint(fd, "pri 13");
-		close(fd);
-	}
-	free(ctl);
-}
-
-void
-decproc(void*)
-{
-	Pmsg playstate, newstate;
-	int fd;
-	Pacbuf *pb;
-	Alt a[3] = {
-		{empty, &pb, CHANNOP},
-		{playc, &newstate.m, CHANRCV},
-		{nil, nil, CHANEND},
-	};
-
-	threadsetname("decproc");
-	close(srvfd[1]);
-	newstate.cmd = playstate.cmd = Stop;
-	newstate.off = playstate.off = 0;
-	fd = -1;
-	for(;;){
-		switch(alt(a)){
-		case 0:
-			/* Play out next buffer (pb points to one already) */
-			assert(fd >= 0);	/* Because we must be in Play mode */
-			pb->m = playstate.m;
-			pb->len = read(fd, pb->data, sizeof pb->data);
-			if(pb->len > 0){
-				sendp(full, pb);
-				break;
-			}
-			if(pb->len < 0){
-				if(debug & DbgPlayer)
-					fprint(2, "pac, error: %d\n", playstate.off);
-				pb->cmd = Error;
-				pb->len = snprint(pb->data, sizeof pb->data, "%s: %r", curfile);
-				sendp(full, pb);
-			}else{
-				/* Simple end of file */
-				sendp(empty, pb); /* Don't need buffer after all */
-			}
-			close(fd);
-			fd = -1;
-			if(debug & DbgPlayer)
-				fprint(2, "pac, eof: %d\n", playstate.off);
-			/* End of file, do next by falling through */
-			newstate.cmd = playstate.cmd;
-			newstate.off = playstate.off + 1;
-		case 1:
-			if((debug & DbgPac) && newstate.cmd)
-				fprint(2, "Pacproc: newstate %s-%d, playstate %s-%d\n",
-					statetxt[newstate.cmd], newstate.off,
-					statetxt[playstate.cmd], playstate.off);
-			/* Deal with an incoming command */
-			if(newstate.cmd == Pause || newstate.cmd == Resume){
-				/* Just pass them on, don't change local state */
-				pb = recvp(spare);
-				pb->m = newstate.m;
-				sendp(full, pb);
-				break;
-			}
-			/* Stop whatever we're doing */
-			if(fd >= 0){
-				if(debug & DbgPlayer)
-					fprint(2, "pac, stop\n");
-				/* Stop any active (pac) decoders */
-				close(fd);
-				fd = -1;
-			}
-			a[0].op = CHANNOP;
-			switch(newstate.cmd){
-			default:
-				sysfatal("decproc: unexpected newstate %d", newstate.cmd);
-			case Stop:
-				/* Wait for state to change */
-				break;
-			case Skip:
-			case Play:
-				fd = startplay(newstate.off);
-				if(fd >=0){
-					playstate = newstate;
-					a[0].op = CHANRCV;
-					continue;	/* Start reading */
-				}
-				newstate.cmd = Stop;
-			}
-			pb = recvp(spare);
-			pb->m = newstate.m;
-			sendp(full, pb);
-			playstate = newstate;
-		}
-	}
-}
-
-void
-pcmproc(void*)
-{
-	Pmsg localstate, newstate, prevstate;
-	int fd, n;
-	Pacbuf *pb, *b;
-	Alt a[3] = {
-		{full, &pb, CHANRCV},
-		{playout, &pb, CHANRCV},
-		{nil, nil, CHANEND},
-	};
-
-	/*
-	 * This is the real-time proc.
-	 * It gets its input from two sources, full data/control buffers from the decproc
-	 * which mixes decoded data with control messages, and data buffers from the pcmproc's
-	 * (*this* proc's) own internal playout buffer.
-	 * When a command is received on the `full' channel containing a command that warrants
-	 * an immediate change of audio source (e.g., to silence or to another number), we just
-	 * toss everything in the pipeline -- i.e., the playout channel
-	 * Finally, we report all state changes using `playupdate' (another message channel)
-	 */
-	threadsetname("pcmproc");
-	close(srvfd[1]);
-	fd = -1;
-	localstate.cmd = 0;	/* Force initial playupdate */
-	newstate.cmd = Stop;
-	newstate.off = 0;
-//	rtsched();
-	boost();
-	for(;;){
-		if(newstate.m != localstate.m){
-			playupdate(newstate, nil);
-			localstate = newstate;
-		}
-		switch(alt(a)){
-		case 0:
-			/* buffer received from decproc */
-			if((debug & DbgPcm) && localstate.m != prevstate.m){
-				fprint(2, "pcm, full: %s-%d, local state is %s-%d\n",
-					statetxt[pb->cmd], pb->off,
-					statetxt[localstate.cmd], localstate.off);
-				prevstate.m = localstate.m;
-			}
-			switch(pb->cmd){
-			default:
-				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
-			case Resume:
-				a[1].op = CHANRCV;
-				newstate.cmd = Play;
-				break;
-			case Pause:
-				a[1].op = CHANNOP;
-				newstate.cmd = Pause;
-				if(fd >= 0){
-					close(fd);
-					fd = -1;
-				}
-				break;
-			case Stop:
-				/* Dump all data in the buffer */
-				while(b = nbrecvp(playout))
-					if(b->cmd == Error){
-						playupdate(b->Pmsg, b->data);
-						sendp(spare, b);
-					}else
-						sendp(empty, b);
-				newstate.m = pb->m;
-				a[1].op = CHANRCV;
-				if(fd >= 0){
-					close(fd);
-					fd = -1;
-				}
-				break;
-			case Skip:
-				/* Dump all data in the buffer, then fall through */
-				while(b = nbrecvp(playout))
-					if(b->cmd == Error){
-						playupdate(pb->Pmsg, pb->data);
-						sendp(spare, pb);
-					}else
-						sendp(empty, b);
-				a[1].op = CHANRCV;
-				newstate.cmd = Play;
-			case Error:
-			case Play:
-				/* deal with at playout, just requeue */
-				sendp(playout, pb);
-				pb = nil;
-				localstate = newstate;
-				break;
-			}
-			/* If we still have a buffer, free it */
-			if(pb)
-				sendp(spare, pb);
-			break;
-		case 1:
-			/* internal buffer */
-			if((debug & DbgPlayer) && localstate.m != prevstate.m){
-				fprint(2, "pcm, playout: %s-%d, local state is %s-%d\n",
-					statetxt[pb->cmd], pb->off,
-					statetxt[localstate.cmd], localstate.off);
-				prevstate.m = localstate.m;
-			}
-			switch(pb->cmd){
-			default:
-				sysfatal("pcmproc: unknown newstate: %s-%d", statetxt[pb->cmd], pb->off);
-			case Error:
-				playupdate(pb->Pmsg, pb->data);
-				localstate = newstate;
-				sendp(spare, pb);
-				break;
-			case Play:
-				if(fd < 0 && (fd = open("/dev/audio", OWRITE)) < 0){
-					a[1].op = CHANNOP;
-					newstate.cmd = Pause;
-					pb->cmd = Error;
-					snprint(pb->data, sizeof(pb->data),
-						"/dev/audio: %r");
-					playupdate(pb->Pmsg, pb->data);
-					sendp(empty, pb);
-					break;
-				}
-				/* play out this buffer */
-				totbytes += pb->len;
-				totbuffers++;
-				n = write(fd, pb->data, pb->len);
-				if (n != pb->len){
-					if (debug & DbgPlayer)
-						fprint(2, "pcmproc: file %d: %r\n", pb->off);
-					if (n < 0)
-						sysfatal("pcmproc: write: %r");
-				}
-				newstate.m = pb->m;
-				sendp(empty, pb);
-				break;
-			}
-			break;
-		}
-	}
-}
-
-void
-playinit(void)
-{
-	int i;
-
-	full = chancreate(sizeof(Pacbuf*), 1);
-	empty = chancreate(sizeof(Pacbuf*), NPacbuf);
-	spare = chancreate(sizeof(Pacbuf*), NSparebuf);
-	playout = chancreate(sizeof(Pacbuf*), NPacbuf+NSparebuf);
-	for(i = 0; i < NPacbuf; i++)
-		sendp(empty, malloc(sizeof(Pacbuf)));
-	for(i = 0; i < NSparebuf; i++)
-		sendp(spare, malloc(sizeof(Pacbuf)));
-
-	playc = chancreate(sizeof(ulong), 1);
-	procrfork(decproc, nil, 32*1024, RFFDG);
-	procrfork(pcmproc, nil, 32*1024, RFFDG);
-}
-
-char *
-getplaystat(char *p, char *e)
-{
-	p = seprint(p, e, "empty buffers %d of %d\n", empty->n, empty->s);
-	p = seprint(p, e, "full buffers %d of %d\n", full->n, full->s);
-	p = seprint(p, e, "playout buffers %d of %d\n", playout->n, playout->s);
-	p = seprint(p, e, "spare buffers %d of %d\n", spare->n, spare->s);
-	p = seprint(p, e, "bytes %lud / buffers %lud played\n", totbytes, totbuffers);
-	return p;
-}
--- a/sys/src/games/music/playlistfs/playlist.h	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,147 +0,0 @@
-typedef struct Worker Worker;
-typedef struct Req Req;
-typedef struct Fid Fid;
-typedef struct File File;
-typedef struct Playlist Playlist;
-typedef struct Wmsg Wmsg;
-typedef union Pmsg Pmsg;
-typedef struct Pacbuf Pacbuf;
-
-enum {
-	Qdir,
-	Qplayctl,
-	Qplaylist,
-	Qplayvol,
-	Qplaystat,
-	Nqid,
-};
-
-enum {
-	DbgPcm		= 0x01000,
-	DbgPac		= 0x02000,
-	DbgFs		= 0x10000,
-	DbgWorker	= 0x20000,
-	DbgPlayer	= 0x40000,
-	DbgError	= 0x80000,
-};
-
-enum {
-	Messagesize = 8*1024+IOHDRSZ,
-	Undef = 0x80000000,
-	/* 256 buffers of 4096 bytes represents 5.9 seconds
-	 * of playout at 44100 Hz (2*16bit samples)
-	 */
-	NPacbuf = 256,
-	Pacbufsize = 4096,
-	NSparebuf = 16,	/* For in-line commands (Pause, Resume, Error) */
-};
-
-enum {
-	/* Named commands (see fs.c): */
-	Nostate,	// can't use zero for state
-	Error,
-	Stop,
-	Pause,
-	Play,
-	Resume,
-	Skip,
-	/* Unnamed commands */
-	Work,
-	Check,
-	Flush,
-	Prep,
-	Preq,
-};
-
-union Pmsg {
-	ulong m;
-	struct{
-		ushort cmd;
-		ushort off;
-	};
-};
-
-struct Wmsg {
-	Pmsg;
-	void	*arg;	/* if(cmd != Work) mallocated by sender, freed by receiver */
-};
-
-struct Playlist {
-	/* The play list consists of a sequence of {objectref, filename}
-	 * entries.  Object ref and file name are separated by a tab.
-	 * An object ref may not contain a tab.  Entries are seperated
-	 * by newline characters.  Neither file names, nor object refs
-	 * may contain newlines.
-	 */
-	ulong	*lines;
-	ulong	nlines;
-	char	*data;
-	ulong	ndata;
-};
-
-struct File {
-	Dir	dir;
-	Channel	*workers;
-};
-
-struct Worker
-{
-	Req	*r;
-	Channel	*eventc;
-};
-
-struct Fid
-{
-	int	fid;
-	File	*file;
-	ushort	flags;
-	short	readers;
-	ulong	vers;	/* set to file's version when completely read */
-	Fid	*next;
-};
-
-struct Req
-{
-	uchar	indata[Messagesize];
-	uchar	outdata[Messagesize];
-	Fcall	ifcall;
-	Fcall	ofcall;
-	Fid*	fid;
-};
-
-struct Pacbuf {
-	Pmsg;
-	int len;
-	char data[Pacbufsize];
-};
-
-void	allocwork(Req*);
-Wmsg	waitmsg(Worker*, Channel*);
-int	sendmsg(Channel*, Wmsg*);
-void	bcastmsg(Channel*, Wmsg*);
-void	reqfree(Req*);
-Req	*reqalloc(void);
-void	readbuf(Req*, void*, long);
-void	readstr(Req*, char*);
-void	volumeset(int *v);
-void	playupdate(Pmsg, char*);
-void	playinit(void);
-void	volumeproc(void*);
-void	srv(void *);
-long	robustread(int, void*, long);
-void	volumeupdate(int*);
-char	*getplaylist(int);
-char	*getplaystat(char*, char*);
-
-extern int		debug, aflag;
-extern char	*user;
-extern Channel	*playc;
-extern char	*statetxt[];
-extern int		volume[8];
-extern Playlist	playlist;
-extern Channel	*workers;
-extern Channel	*volumechan;
-extern Channel	*playchan;
-extern Channel	*playlistreq;
-extern File	files[];
-extern int		srvfd[];
--- a/sys/src/games/music/playlistfs/playplumb.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,20 +0,0 @@
-#include <u.h>
-#include <libc.h>
-
-void
-main(int argc, char *argv[])
-{
-	Plumbmsg *m;
-	int fd;
-
-	fd = plumbopen("audioplay", OREAD);
-	if (fd < 0)
-		sysfatal("port audioplay: %r");
-	for (;;) {
-		m = plumbrecv(fd);
-		if (m == nil)
-			sysfatal("plumrecv: %r");
-		
-		plumbfree(m);
-	}
-}
--- a/sys/src/games/music/playlistfs/volume.c	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,91 +0,0 @@
-#include <u.h>
-#include <libc.h>
-#include <thread.h>
-#include <fcall.h>
-#include "pool.h"
-#include "playlist.h"
-
-int	minvolume, maxvolume;
-
-void
-volumeproc(void *)
-{
-	int fd, n, nf, i, nlines;
-	static char buf[1024];
-	char *lines[32];
-	char *fields[8];
-	char *subfields[9];
-	int volume[8], nvolumes;
-
-	threadsetname("volumeproc");
-	close(srvfd[1]);
-	fd = open("/dev/audioctl", OREAD);
-	if (fd < 0)
-		threadexits(nil);
-	for(;;){
-		n = read(fd, buf, sizeof buf -1);
-		if (n == 0)
-			continue;
-		if (n < 0){
-			fprint(2, "volumeproc: read: %r\n");
-			threadexits("volumeproc");
-		}
-		buf[n] = '\0';
-		nlines = getfields(buf, lines, nelem(lines), 1, "\n");
-		for(i = 0; i < nlines; i++){
-			nf = tokenize(lines[i], fields, nelem(fields));
-			if (nf == 0)
-				continue;
-			if (nf != 6 || strcmp(fields[0], "volume") || strcmp(fields[1], "out"))
-				continue;
-			minvolume = strtol(fields[3], nil, 0);
-			maxvolume = strtol(fields[4], nil, 0);
-			if (minvolume >= maxvolume)
-				continue;
-			nvolumes = tokenize(fields[2], subfields, nelem(subfields));
-			if (nvolumes <= 0 || nvolumes > 8)
-				sysfatal("bad volume data");
-			if (debug)
-				fprint(2, "volume changed to '");
-			for (i = 0; i < nvolumes; i++){
-				volume[i] = strtol(subfields[i], nil, 0);
-				volume[i]= 100*(volume[i]- minvolume)/(maxvolume-minvolume);
-				if (debug)
-					fprint(2, " %d", volume[i]);
-			}
-			if (debug)
-				fprint(2, "'\n");
-			while (i < 8)
-				volume[i++] = Undef;
-			send(volumechan, volume);
-		}
-	}
-}
-
-void
-volumeset(int *v)
-{
-	int fd, i;
-	char buf[256], *p;
-
-	fd = open("/dev/audioctl", OWRITE);
-	if (fd < 0){
-		fd = open("/dev/volume", OWRITE);
-		if (fd < 0){
-			fprint(2, "Can't set volume: %r");
-			return;
-		}
-		fprint(fd, "audio out %d",  v[0]);
-		send(volumechan, v);
-	} else {
-		p = buf;
-		for (i = 0; i < 8; i++){
-			if (v[i] == Undef) break;
-			p = seprint(p, buf+sizeof buf, (p==buf)?"volume out '%d":" %d",
-				minvolume + v[i] * (maxvolume-minvolume) / 100);
-		}
-		p = seprint(p, buf+sizeof buf, "'\n");
-		write(fd, buf, p-buf);
-	}
-	close(fd);
-}
--- a/sys/src/games/music/readcd	Tue Apr 13 07:20:27 2021
+++ /dev/null	Sun Oct 31 09:33:26 2021
@@ -1,46 +0,0 @@
-#!/bin/rc
-
-cdrom=/dev/sdC1
-
-switch($#*){
-case 3
-	starttrack = `{echo $1 - 1 | hoc}
-	endtrack = `{echo $2 - 1 | hoc}
-	desttrack = $3
-case *
-	echo Usage readcd starttrack endtrack desttrack
-}
-
-if(test -e /mnt/cd/ctl){
-	echo -n ingest >/mnt/cd/ctl >[2]/dev/null
-}
-if not {
-	if (~ $cdrom '')	cdfs
-	if not		cdfs -d $cdrom
-}
-
->/tmp/readcd
->/tmp/map
-cat /mnt/cd/ctl
-sed 1q /mnt/cd/ctl | rc
-echo $starttrack $endtrack $desttrack | awk '{
-	start = $1
-	finish = $2
-	dest = $3
-	print "read cd tracks " start "-" finish " starting at " dest
-	for (i = start; i <= finish; i++) {
-		cmd = sprintf("ls -l /mnt/cd/a%3.3d | awk ''{print $6}''>>/tmp/readcd", i)
-		system(cmd)
-		getline x<"/tmp/readcd"
-		sec = x/44100/4
-		min = sec/60
-		sec = sec%60
-		printf("track {\n\t\n\tfile {%3.3d}\n\ttime {%d:%2.2d}\n}\n",i+dest-start,min,sec)>"/tmp/map"
-	}
-	for (i = start; i <= finish; i++) {
-		cmd = sprintf("/bin/games/pacenc /mnt/cd/a%3.3d %3.3d",i,i+dest-start)
-		print cmd
-		system(cmd)
-	}
-}'
-echo eject >/mnt/cd/ctl