mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
remove juke (use play or zuke instead)
This commit is contained in:
parent
c6cdee420d
commit
c613382caf
50 changed files with 0 additions and 6677 deletions
57
rc/bin/juke
57
rc/bin/juke
|
@ -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
|
368
sys/man/7/juke
368
sys/man/7/juke
|
@ -1,368 +0,0 @@
|
|||
.TH JUKE 7
|
||||
.SH NAME
|
||||
juke \- music jukebox
|
||||
.SH SYNOPSIS
|
||||
.B juke
|
||||
[
|
||||
.B \-t
|
||||
]
|
||||
[
|
||||
.B \-w
|
||||
]
|
||||
[
|
||||
.B \-h
|
||||
.I srvhost
|
||||
]
|
||||
[
|
||||
.B \-s
|
||||
.I srvname
|
||||
]
|
||||
.ift .sp 0.5
|
||||
.ifn .sp
|
||||
.B games/jukebox
|
||||
[
|
||||
.B \-t
|
||||
]
|
||||
[
|
||||
.B \-w
|
||||
]
|
||||
.ift .sp 0.5
|
||||
.ifn .sp
|
||||
.B games/jukefs
|
||||
[
|
||||
.B \-m
|
||||
.I mountpoint
|
||||
]
|
||||
[
|
||||
.B \-s
|
||||
.I srvname
|
||||
]
|
||||
[
|
||||
.I mapfile
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Jukebox
|
||||
controls a playlist server
|
||||
(see
|
||||
.IR playlistfs (7))
|
||||
through a graphical user interface. It connects to a music database server which reads a set of
|
||||
.I map
|
||||
files that describe recordings and their location. Currently, there is
|
||||
one set of maps, mostly for classical music, with some jazz and other stuff
|
||||
thrown in. These are served by
|
||||
.BR jukefs ,
|
||||
which presents a file system conventionally mounted at
|
||||
.BR /mnt/juke .
|
||||
The playlist, explained below, is managed by a file system implemented by
|
||||
.IR playlistfs (7)
|
||||
and normally mounted on
|
||||
.BR /mnt .
|
||||
.PP
|
||||
.I Jukebox
|
||||
is most easily started through the
|
||||
.I juke
|
||||
shell script.
|
||||
.PP
|
||||
.I Jukebox
|
||||
has four windows, which can be selected by clicking the appropriate tab
|
||||
at the top of the window.
|
||||
.PP
|
||||
Above the tab are nine buttons and a volume slider. The
|
||||
.ift buttons, shown below,
|
||||
.ifn buttons
|
||||
are named, from left to right,
|
||||
.IR Exit ,
|
||||
.IR Pause ,
|
||||
.IR Play ,
|
||||
.IR Halt ,
|
||||
.IR Back ,
|
||||
.IR Forward ,
|
||||
.IR Root ,
|
||||
.IR Delete ,
|
||||
and
|
||||
.IR Help .
|
||||
The buttons are
|
||||
.I active
|
||||
when they are displayed in dark green (or red). When they are pale blue
|
||||
they are
|
||||
.IR inactive .
|
||||
The Exit button is always active; it exits the program (but leaves the playlist and music database
|
||||
servers running).
|
||||
.PP
|
||||
The
|
||||
.I browse
|
||||
window is for browsing through the music and selecting music to play.
|
||||
Browsing down in the music hierarchy is done by clicking button one on
|
||||
an item. Clicking button three goes back up.
|
||||
Clicking button two recursively adds all files below the selected item to
|
||||
the
|
||||
.IR "play list" .
|
||||
.PP
|
||||
The selected music is displayed in the
|
||||
.I playlist
|
||||
window.
|
||||
The track currently playing is shown in the
|
||||
.I playing
|
||||
window.
|
||||
.PP
|
||||
The
|
||||
.I Root
|
||||
button browses back to the root.
|
||||
.PP
|
||||
The
|
||||
.I Delete
|
||||
button empties the playlist.
|
||||
.PP
|
||||
The
|
||||
.I Help
|
||||
displays a minimal on-line manual.
|
||||
.PP
|
||||
.I Play
|
||||
starts playing at the beginning of the play list, or at the selected track in
|
||||
the play list.
|
||||
.PP
|
||||
During play,
|
||||
.IR Pause ,
|
||||
.IR Stop ,
|
||||
.IR Back ,
|
||||
and
|
||||
.I Forward
|
||||
are active.
|
||||
.I Back
|
||||
and
|
||||
.I Forward
|
||||
go back or forward a track at a time. The other buttons do the obvious thing.
|
||||
.PP
|
||||
The
|
||||
.B \-t
|
||||
flag chooses a tiny font, useful for handhelds.
|
||||
.PP
|
||||
The
|
||||
.B \-w
|
||||
flag creates the jukebox in a new window. Normally, the jukebox takes over
|
||||
the window in which it is invoked.
|
||||
.PP
|
||||
The
|
||||
.B \-s
|
||||
flag specifies the name under which the file descriptors of the playlist and databse servers are posted
|
||||
in /srv. This allows two or more play list servers to exist on one platform, e.g., when
|
||||
there are several audio devices. The default value of the flag is
|
||||
.B $\f2user\fP
|
||||
for a playlist server at
|
||||
.B /srv/playlistfs.$\f2user\fP
|
||||
and a database server at
|
||||
.BR /srv/jukefs.$\f2user\fP .
|
||||
.sp
|
||||
.LP
|
||||
.B Jukefs
|
||||
reads a set of
|
||||
.I maps
|
||||
describing the music data, builds an in-memory database, and provides
|
||||
lookup service to
|
||||
.IR jukebox .
|
||||
The default map is
|
||||
.BR /sys/lib/music/map .
|
||||
It consists of a hierarchical set of
|
||||
.IR objects .
|
||||
Each object has a type, a value, zero or more attribute-value
|
||||
pairs and zero or more subobjects. An object consists of the
|
||||
type, followed by its contents between curly brackets.
|
||||
Attribute value pairs consist
|
||||
of a single line containing an attribute name, an equals sign, and
|
||||
a value.
|
||||
The value of an object is any text not containing curly brackets or equals
|
||||
signs. Here is an example:
|
||||
.EX
|
||||
.ps -2
|
||||
.vs -2p
|
||||
.sp
|
||||
category {
|
||||
composer = mahler
|
||||
|
||||
Gustav Mahler
|
||||
(1860 — 1911)
|
||||
|
||||
work {
|
||||
path {classic/mahler}
|
||||
class = symphonic
|
||||
orchestra = rfo
|
||||
conductor = Waart,~Edo~de
|
||||
|
||||
Symphony Nº 5 in c♯ (RFO, Vienna)
|
||||
performance{
|
||||
Radio Filharmonisch Orkest Holland
|
||||
Edo de Waart, conductor
|
||||
|
||||
recorded: Musikverein, Vienna, May 6, 1996
|
||||
}
|
||||
command {number}
|
||||
track {
|
||||
Trauermarsch (In gemessenem Schritt. Streng. Wie ein Kondukt)
|
||||
time {13:55}
|
||||
file {034.pac}
|
||||
}
|
||||
track {
|
||||
Stürmisch bewegt, mit größter Vehemenz
|
||||
time {15:34}
|
||||
file {035.pac}
|
||||
}
|
||||
track {
|
||||
Scherzo (Kräftig, nicht zu schnell)
|
||||
time {18:54}
|
||||
file {036.pac}
|
||||
}
|
||||
track {
|
||||
Adagietto (Sehr Langsam)
|
||||
time {10:01}
|
||||
file {037.pac}
|
||||
}
|
||||
track {
|
||||
Rondo–Finale (Allegro)
|
||||
time {15:44}
|
||||
file {038.pac}
|
||||
}
|
||||
}
|
||||
}
|
||||
.EE
|
||||
.LP
|
||||
This example shows a
|
||||
.I category
|
||||
object for the composer Gustav Mahler (the value consists of the two
|
||||
lines `Gustav Mahler' and `(1860 — 1911)') with one subobject, a
|
||||
.I work
|
||||
object whose value is `Symphony Nº 5 in c♯ (RFO, Vienna)'. The work object
|
||||
contains six subobjects: one
|
||||
.I performance
|
||||
object and five
|
||||
.I track
|
||||
objects.
|
||||
.PP
|
||||
.I Category
|
||||
objects must contain exactly one attribute-value pair. The attribute
|
||||
names a subobject of the root under which this category object will
|
||||
be placed. Gustav Mahler, thus, will be placed in
|
||||
Root→composer.
|
||||
.IR Work ,
|
||||
.IR Recording ,
|
||||
.IR Part ,
|
||||
and
|
||||
.IR Track ,
|
||||
objects all describe named containers for subunits.
|
||||
A
|
||||
.IR Lyrics ,
|
||||
.IR Performance ,
|
||||
or
|
||||
.IR Soloists
|
||||
object adds information to a
|
||||
.IR Work ,
|
||||
.IR Recording ,
|
||||
.IR Part ,
|
||||
or
|
||||
.IR Track ,
|
||||
object. It should only contain text.
|
||||
The same is true for a
|
||||
.I Time
|
||||
object; however, it should only be used adjacent to
|
||||
.I File
|
||||
objects and it should contain the running time of that file (this
|
||||
is for future use).
|
||||
.PP
|
||||
A
|
||||
.I File
|
||||
object specifies a file to be played. When the
|
||||
.I Select
|
||||
button is pressed, all file objects contained hierarchically in the
|
||||
selected object are added to the playlist.
|
||||
.PP
|
||||
There are a number of pseudo objects:
|
||||
.I Command
|
||||
may contain either
|
||||
.I sort
|
||||
or
|
||||
.IR number .
|
||||
The
|
||||
.I sort
|
||||
command sorts the subobjects of the object it appears in by
|
||||
.I key
|
||||
or textual content.
|
||||
The
|
||||
.I number
|
||||
commands prepends numbers to the texts of its subobjects
|
||||
(e.g., for the parts in a symphony)
|
||||
.PP
|
||||
An
|
||||
.I Include
|
||||
object is replaced by the contents of the named file.
|
||||
.PP
|
||||
A
|
||||
.I Key
|
||||
object specifies a key for sorting subobjects.
|
||||
.PP
|
||||
Finally, a
|
||||
.I Path
|
||||
object specifies a path to be prepended to the files named in
|
||||
hierarchically contained
|
||||
.I File
|
||||
objects.
|
||||
.PP
|
||||
The attribute-value value pairs arrange for entries to be made of the
|
||||
current object in a
|
||||
.I Category
|
||||
object named by the attribute directly under the root.
|
||||
.sp
|
||||
.LP
|
||||
The interface to the browsing database is through a file system
|
||||
implemented by
|
||||
.BR jukefs .
|
||||
The file system synthesises a directory per object. Each directory contains a set of files
|
||||
describing the object's attributes:
|
||||
.TP
|
||||
.B children
|
||||
contains a new-line separated list of subobject names. For each name,
|
||||
.I x
|
||||
the directory
|
||||
.BI /mnt/juke/ x
|
||||
describes the subobject.
|
||||
.TP
|
||||
.B digest
|
||||
contains a one-line summary of the object
|
||||
.TP
|
||||
.B files
|
||||
is a new-line separated list of file objects contained in this object.
|
||||
Each line consists of object name and file name.
|
||||
.TP
|
||||
.B fulltext
|
||||
is the fulltextual value of the object.
|
||||
.TP
|
||||
.B key
|
||||
contains the key by which objects are sorted
|
||||
.TP
|
||||
.B miniparentage
|
||||
is a one-line summary of the objects and the path leading to it from the root.
|
||||
This is the line displayed in the playlist and bottom browse windows of
|
||||
.BR games/jukebox .
|
||||
.TP
|
||||
.B parent
|
||||
is the object reference to the parent of this object.
|
||||
.TP
|
||||
.B parentage
|
||||
is a full description of the path leading to this object and the object itself.
|
||||
This is the string displayed in the top of the Browse and Playing windows
|
||||
of
|
||||
.BR games/jukebox .
|
||||
.TP
|
||||
.B text
|
||||
is the text field of the object.
|
||||
.TP
|
||||
.B type
|
||||
is the type of the object
|
||||
.LP
|
||||
.SH FILES
|
||||
.BR /sys/lib/music/map :
|
||||
Default map file
|
||||
.BR /mnt/juke :
|
||||
Default mount point for the music database.
|
||||
.SH SOURCE
|
||||
.B /sys/src/games/music
|
||||
.SH SEE ALSO
|
||||
.IR playlistfs (7).
|
|
@ -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).
|
|
@ -40,7 +40,6 @@ DIRS=\
|
|||
mahjongg\
|
||||
mines\
|
||||
mix\
|
||||
music\
|
||||
md\
|
||||
nes\
|
||||
opl3\
|
||||
|
|
|
@ -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
|
|
@ -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.
Binary file not shown.
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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*);
|
|
@ -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");
|
||||
}
|
|
@ -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*);
|
|
@ -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
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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*);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#include <regexp.h>
|
||||
|
||||
extern Object *sobj;
|
||||
|
||||
Object *search(Object *rt, Object *parent, Reprog *preg);
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -1,9 +0,0 @@
|
|||
base = ..
|
||||
CFLAGS=$CFLAGS $DEFINES
|
||||
|
||||
all:
|
||||
|
||||
dep:Q:
|
||||
mkdep $INCLUDES $CFILES > mk.dep
|
||||
|
||||
< mk.dep
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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[];
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
Loading…
Reference in a new issue