remove juke (use play or zuke instead)

This commit is contained in:
Sigrid 2021-04-13 13:25:24 +02:00
parent c6cdee420d
commit c613382caf
50 changed files with 0 additions and 6677 deletions

View file

@ -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

View file

@ -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 {
RondoFinale (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).

View file

@ -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).

View file

@ -40,7 +40,6 @@ DIRS=\
mahjongg\
mines\
mix\
music\
md\
nes\
opl3\

View file

@ -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

View file

@ -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 file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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

View file

@ -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;
}

View file

@ -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*);

View file

@ -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");
}

View file

@ -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*);

View file

@ -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

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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*);

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -1,5 +0,0 @@
#include <regexp.h>
extern Object *sobj;
Object *search(Object *rt, Object *parent, Reprog *preg);

View file

@ -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;
}

View file

@ -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

View file

@ -1,9 +0,0 @@
base = ..
CFLAGS=$CFLAGS $DEFINES
all:
dep:Q:
mkdep $INCLUDES $CFILES > mk.dep
< mk.dep

View file

@ -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));
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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[];

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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