#include "types.h"
#include <string.h>
+#include <assert.h>
+#include <stdio.h>
#include "filepart.h"
#include "mem.h"
+/** @brief Parse a filename
+ * @param path Filename to parse
+ * @param Where to put directory name, or NULL
+ * @param Where to put basename, or NULL
+ */
+static void parse_filename(const char *path,
+ char **dirnamep,
+ char **basenamep) {
+ const char *s, *e = path + strlen(path);
+
+ /* Strip trailing slashes. We never take these into account. */
+ while(e > path && e[-1] == '/')
+ --e;
+ if(e == path) {
+ /* The path is empty or contains only slashes */
+ if(*path) {
+ if(dirnamep)
+ *dirnamep = xstrdup("/");
+ if(basenamep)
+ *basenamep = xstrdup("/");
+ } else {
+ if(dirnamep)
+ *dirnamep = xstrdup("");
+ if(basenamep)
+ *basenamep = xstrdup("");
+ }
+ } else {
+ /* The path isn't empty and has more than just slashes. e therefore now
+ * points at the end of the basename. */
+ s = e;
+ while(s > path && s[-1] != '/')
+ --s;
+ /* Now s points at the start of the basename */
+ if(basenamep)
+ *basenamep = xstrndup(s, e - s);
+ if(s > path) {
+ --s;
+ /* s must now be pointing at a '/' before the basename */
+ assert(*s == '/');
+ while(s > path && s[-1] == '/')
+ --s;
+ /* Now s must be pointing at the last '/' after the dirname */
+ assert(*s == '/');
+ if(s == path) {
+ /* If we reached the start we must be at the root */
+ if(dirnamep)
+ *dirnamep = xstrdup("/");
+ } else {
+ /* There's more than just the root here */
+ if(dirnamep)
+ *dirnamep = xstrndup(path, s - path);
+ }
+ } else {
+ /* There wasn't a slash */
+ if(dirnamep)
+ *dirnamep = xstrdup(".");
+ }
+ }
+}
+
/** @brief Return the directory part of @p path
* @param path Path to parse
* @return Directory part of @p path
*
* Extracts the directory part of @p path. This is a simple lexical
* transformation and no canonicalization is performed. The result will only
- * ever end "/" if it is the root directory.
+ * ever end "/" if it is the root directory. The result will be "." if there
+ * is no directory part.
*/
char *d_dirname(const char *path) {
- const char *s;
+ char *d = 0;
- if((s = strrchr(path, '/'))) {
- while(s > path && s[-1] == '/')
- --s;
- if(s == path)
- return xstrdup("/");
- else
- return xstrndup(path, s - path);
- } else
- return xstrdup(".");
+ parse_filename(path, &d, 0);
+ assert(d != 0);
+ return d;
+}
+
+/** @brief Return the basename part of @p path
+ * @param Path to parse
+ * @return Base part of @p path
+ *
+ * Extracts the base part of @p path. This is a simple lexical transformation
+ * and no canonicalization is performed. The result is always newly allocated
+ * even if compares equal to @p path.
+ */
+char *d_basename(const char *path) {
+ char *b = 0;
+
+ parse_filename(path, 0, &b);
+ assert(b != 0);
+ return b;
}
/** @brief Find the extension part of @p path
#ifndef FILEPART_H
#define FILEPART_H
+char *d_basename(const char *path);
+
char *d_dirname(const char *path);
/* return the directory name part of @path@ */
#include "split.h"
#include "printf.h"
#include "vector.h"
+#include "filepart.h"
static struct vector include_path;
return 0;
}
+/* @basename{PATH}
+ *
+ * Expands to the UNQUOTED basename of PATH.
+ */
+static int exp_basename(int attribute((unused)) nargs,
+ char **args,
+ struct sink attribute((unused)) *output,
+ void attribute((unused)) *u) {
+ return sink_writes(output, d_basename(args[0])) < 0 ? -1 : 0;
+}
+
+/* @dirname{PATH}
+ *
+ * Expands to the UNQUOTED directory name of PATH.
+ */
+static int exp_dirname(int attribute((unused)) nargs,
+ char **args,
+ struct sink attribute((unused)) *output,
+ void attribute((unused)) *u) {
+ return sink_writes(output, d_dirname(args[0])) < 0 ? -1 : 0;
+}
+
/** @brief Register built-in expansions */
void mx_register_builtin(void) {
- mx_register_magic("#", 0, INT_MAX, exp_comment);
- mx_register_magic("and", 0, INT_MAX, exp_and);
- mx_register_magic("define", 3, 3, exp_define);
- mx_register_magic("if", 2, 3, exp_if);
- mx_register_magic("or", 0, INT_MAX, exp_or);
+ mx_register("basename", 1, 1, exp_basename);
+ mx_register("dirname", 1, 1, exp_dirname);
mx_register("discard", 0, INT_MAX, exp_discard);
mx_register("eq", 0, INT_MAX, exp_eq);
mx_register("include", 1, 1, exp_include);
mx_register("not", 1, 1, exp_not);
mx_register("shell", 1, 1, exp_shell);
mx_register("urlquote", 1, 1, exp_urlquote);
+ mx_register_magic("#", 0, INT_MAX, exp_comment);
+ mx_register_magic("and", 0, INT_MAX, exp_and);
+ mx_register_magic("define", 3, 3, exp_define);
+ mx_register_magic("if", 2, 3, exp_if);
+ mx_register_magic("or", 0, INT_MAX, exp_or);
}
/** @brief Add a directory to the search path
*/
#include "test.h"
+#define check_filepart(PATH, DIR, BASE) do { \
+ char *d = d_dirname(PATH), *b = d_basename(PATH); \
+ \
+ if(strcmp(d, DIR)) { \
+ fprintf(stderr, "%s:%d: d_dirname failed:\n" \
+ " path: %s\n" \
+ " got: %s\n" \
+ "expected: %s\n", \
+ __FILE__, __LINE__, \
+ PATH, d, DIR); \
+ count_error(); \
+ } \
+ if(strcmp(b, BASE)) { \
+ fprintf(stderr, "%s:%d: d_basename failed:\n" \
+ " path: %s\n" \
+ " got: %s\n" \
+ "expected: %s\n", \
+ __FILE__, __LINE__, \
+ PATH, d, DIR); \
+ count_error(); \
+ } \
+} while(0)
+
static void test_filepart(void) {
- check_string(d_dirname("/"), "/");
- check_string(d_dirname("////"), "/");
- check_string(d_dirname("/spong"), "/");
- check_string(d_dirname("////spong"), "/");
- check_string(d_dirname("/foo/bar"), "/foo");
- check_string(d_dirname("////foo/////bar"), "////foo");
- check_string(d_dirname("./bar"), ".");
- check_string(d_dirname(".//bar"), ".");
- check_string(d_dirname("."), ".");
- check_string(d_dirname(".."), ".");
- check_string(d_dirname("../blat"), "..");
- check_string(d_dirname("..//blat"), "..");
- check_string(d_dirname("wibble"), ".");
+ check_filepart("", "", "");
+ check_filepart("/", "/", "/");
+ check_filepart("////", "/", "/");
+ check_filepart("/spong", "/", "spong");
+ check_filepart("/spong/", "/", "spong");
+ check_filepart("/spong//", "/", "spong");
+ check_filepart("////spong", "/", "spong");
+ check_filepart("/foo/bar", "/foo", "bar");
+ check_filepart("/foo/bar/", "/foo", "bar");
+ check_filepart("////foo/////bar", "////foo", "bar");
+ check_filepart("./bar", ".", "bar");
+ check_filepart(".//bar", ".", "bar");
+ check_filepart(".", ".", ".");
+ check_filepart("..", ".", "..");
+ check_filepart("../blat", "..", "blat");
+ check_filepart("..//blat", "..", "blat");
+ check_filepart("wibble", ".", "wibble");
check_string(extension("foo.c"), ".c");
check_string(extension(".c"), ".c");
check_string(extension("."), ".");
fprintf(stderr, ">>> expect error message about 'if':\n");
check_macro("badex3", "<@if{1}{2}{3}{4}{5}>",
"<[['if' too many args]]>", 0);
+
+ check_macro("dirname1", "@dirname{foo/bar}", "foo", 0);
+ check_macro("dirname2", "@dirname{foo & something/bar}",
+ "foo & something", 0);
+ check_macro("basename1", "@basename{xyzzy/plugh}", "plugh", 0);
+ check_macro("basename2", "@basename{xyzzy/a<b}", "a<b", 0);
/* Macro definitions ------------------------------------------------------ */
/** @file lib/test.c @brief Library tests */
#include "test.h"
+#include "version.h"
+#include <getopt.h>
long long tests, errors;
int fail_first;
+int verbose;
void count_error(void) {
++errors;
struct dynstr d;
int c;
char buf[10];
-
+
dynstr_init(&d);
while((c = (unsigned char)*s++)) {
if(c >= ' ' && c <= '~')
struct dynstr d;
uint32_t c;
char buf[64];
-
+
dynstr_init(&d);
while((c = *s++)) {
sprintf(buf, " %04lX", (long)c);
return s;
}
+static const struct option options[] = {
+ { "verbose", no_argument, 0, 'v' },
+ { "fail-first", no_argument, 0, 'F' },
+ { "help", no_argument, 0, 'h' },
+ { "version", no_argument, 0, 'V' },
+};
+
+/* display usage message and terminate */
+static void help(void) {
+ xprintf("Usage:\n"
+ " %s [OPTIONS]\n"
+ "Options:\n"
+ " --help, -h Display usage message\n"
+ " --version, -V Display version number\n"
+ " --verbose, -v Verbose output\n"
+ " --fail-first, -F Stop on first failure\n",
+ progname);
+ xfclose(stdout);
+ exit(0);
+}
+
+void test_init(int argc, char **argv) {
+ int n;
+
+ set_progname(argv);
+ mem_init();
+ while((n = getopt_long(argc, argv, "vFhV", options, 0)) >= 0) {
+ switch(n) {
+ case 'v': verbose = 1; break;
+ case 'F': fail_first = 1; break;
+ case 'h': help();
+ case 'V': version(progname);
+ default: exit(1);
+ }
+ }
+ if(getenv("FAIL_FIRST"))
+ fail_first = 1;
+}
+
+
/*
Local Variables:
c-basic-offset:2
extern long long tests, errors;
extern int fail_first;
+extern int verbose;
/** @brief Checks that @p expr is nonzero */
#define insist(expr) do { \
const char *format_utf32(const uint32_t *s);
uint32_t *ucs4parse(const char *s);
const char *do_printf(const char *fmt, ...);
+void test_init(int argc, char **argv);
#define TEST(name) \
- int main(void) { \
- mem_init(); \
- fail_first = !!getenv("FAIL_FIRST"); \
+ int main(int argc, char **argv) { \
+ test_init(argc, argv); \
test_##name(); \
- if(errors) \
+ if(errors || verbose) \
fprintf(stderr, "test_"#name": %lld errors out of %lld tests\n", \
errors, tests); \
return !!errors; \
"\n",
refresh, url, dcgi_cookie_header()) < 0)
fatal(errno, "error writing to stdout");
- dcgi_expand(action ? action : "playing");
+ dcgi_expand("playing");
}
static void act_disable(void) {
configfile = xstrdup(conf);
if(getenv("DISORDER_DEBUG"))
debugging = 1;
- if(config_read(0))
+ /* Read configuration */
+ if(config_read(0/*!server*/))
exit(EXIT_FAILURE);
/* Figure out our URL. This can still be overridden from the config file if
* necessary but it shouldn't be necessary in ordinary installations. */
/* Create the initial connection, trying the cookie if we found a suitable
* one. */
dcgi_login();
- /* The main program... */
+ /* Do whatever the user wanted */
dcgi_action(NULL);
/* In practice if a write fails that probably means the web server went away,
* but we log it anyway. */
struct sink *output,
void attribute((unused)) *u) {
const char *s = cgi_get(args[0]);
+
if(s)
return sink_writes(output,
cgi_sgmlquote(s)) < 0 ? -1 : 0;
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
-@define {action} {}
- {@if {@arg:mgmt@}
- {manage}
- {@arg:action@}@}@
+@define {ifmanage} {yes} {no}
+ {@if {@eq {@arg{action}}{manage}}
+ {@yes}
+ {@no}}
}@@@
-@include{macros.tmpl}@#
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<!--
This file is part of DisOrder.
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
-->
+@discard{
+ @define {ifmanage} {yes no}
+ {@if {@eq {@arg{action}}{manage}}
+ {@yes}
+ {@no}}
+ @define {back} {}
+ {@ifmanage{&back=manage}{}}
+ @include{macros.tmpl}
+}@#
<html>
<head>
-@include{stdhead.tmpl}
+@include{stdhead.tmpl}@#
<title>@if{@isplaying}
- {@part{@playing}{title}@label{playing.title}}</title>
+ {@playing{@part{@id}{title}}}
+ {@label{playing.title}}</title>
</head>
<body>
@include{topbar.tmpl}
<h1>@label{playing.title}</h1>
@# Extra control buttons for the management page
- @if{@arg{mgmt}}{
+ @ifmanage{
<div class=mgmt>
<p class=mgmt>
@if{@paused}{
@# Paused
<a class=button
- href="@url?action=resume&mgmt=true"
+ href="@url?action=resume@back"
title="@label{playing.resumeverbose}">@label{playing.pause}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{enabled}">
}{
@# Not paused
<a class=button
- href="@url?action=pause&mgmt=true"
+ href="@url?action=pause@back"
title="@label{playing.pauseverbose}">@label{playing.pause}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{disabled}">
@if{@random-enabled}{
@# Random play enabled
<a class=button
- href="@url?action=random-disable&mgmt=true"
+ href="@url?action=random-disable@back"
title="@label{playing.randomdisableverbose}">@label{playing.random}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{enabled}">
}{
@# Random play disabled
<a class=button
- href="@url?action=random-enable&mgmt=true"
+ href="@url?action=random-enable@back"
title="@label{playing.randomenableverbose}">@label{playing.random}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{disabled}">
@if{@enabled}{
@# Play enabled
<a class=button
- href="@url?action=disable&mgmt=true"
+ href="@url?action=disable@back"
title="@label{playing.disableverbose}">@label{playing.playing}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{enabled}">
}{
-@# Play disbaled
+@# Play disabled
<a class=button
- href="@url?action=enable&mgmt=true"
+ href="@url?action=enable@back"
title="@label{playing.enableverbose}">@label{playing.playing}</a>
</a>
<img width=16 height=16 class=imgbutton src="@image{disabled}">
}
-@3 Volume form
+@# Volume form
<form class=volume action="@url" method=POST
enctype="multipart/form-data" accept-charset=utf-8>
<span class=volume>
@# Volume up button
@right{volume}{
<a class=imgbutton
- href="@url?action=volume&delta=-@label{volume.resolution}&back=manage">
+ href="@url?action=volume&delta=-@label{volume.resolution}@back">
<img class=button src="@image{down}"
alt="@label{volume.reduce}"
title="@label{volume.reduceverbose}">
@# Volume value widgets
@label{volume.left} <input size=3 name=left type=text value="@volume:left@">
@label{volume.right} <input size=3 name=right type=text value="@volume:right@">
- <input name=back type=hidden value="@thisurl@?mgmt=true">
+ <input name=back type=hidden value="@thisurl@?back=manage">
@# Volume set button
@right{volume}{
<button class=search name=submit type=submit>
@# Volume down button
@right{volume}{
<a class=imgbutton
- href="@url?action=volume&delta=@label{volume.resolution}&back=manage">
+ href="@url?action=volume&delta=@label{volume.resolution}@back">
<img class=button
src="@image{up}"
alt="@label{volume.increase}"
</span>
</p>
</div>
- }
+ }{}
@# Only display the table if there is something to put in it
@if{@or{@isplaying}{@isqueue}}{
<th class=title>@label{heading.title}</th>
<th class=length>@label{heading.length}</th>
<th class=button> </th>
- @if{@arg{mgmt}}{
+ @ifmanage{
<th class=imgbutton> </th>
<th class=imgbutton> </th>
<th class=imgbutton> </th>
<th class=imgbutton> </th>
- }@
+ }{}
</tr>
- @if{@isplaying}{
+ @playing{
<tr class=nowplaying>
- <td colspan=@if{@arg{mgmt}}{11}{7}@>@label{playing.now}</td>
+ <td colspan=@ifmanage{11}{7}>@label{playing.now}</td>
</tr>
- @playing{
<tr class=playing>
- <td class=when>@when@</td>
- <td class=who>@if{@eq{@who@}{}@}{@if{@eq{@state@}{random}@}{@label{playing.randomtrack}}{ }@}{@who@}@</td>
- <td class=artist>@right{play}{<a class=directory
- href="@url?action=choose&directory=@urlquote{@dirname{@dirname{@part:path@}@}@}@"
- title="@label{playing.artistverbose}"
- >@part{short}{artist}@</a>}{<span class=directory
- title="@part{artist}@"
- >@part{short}{artist}@</span>}@</td>
- <td class=album>@right{play}{<a class=directory
- href="@url?action=choose&directory=@urlquote{@dirname{@part:path@}@}@"
- title="@label{playing.albumverbose}"
- >@part{short}{album}@</a>}{<span class=directory
- title="@part{album}@"
- >@part{short}{album}@</span>}@</td>
- <td class=title><span
- title="@part{title}@">@part{short}{title}@</span></td>
- <td class=length>@length@</td>
- <td class=imgbutton>@if{@scratchable@}{<a class=imgbutton
- href="@url?action=scratch&id=@id@&mgmt=@arg{mgmt}"><img
- class=button src="@image{scratch}"
- title="@label{playing.scratchverbose}"
- alt="@label{playing.scratch}"></a>}{<img
- class=button src="@image{noscratch}"
- title="@label{playing.scratchverbose}"
- alt="@label{playing.scratch}">}@</td>
- @if{@arg{mgmt}}{
+ <td class=when>@when{@id}</td>
+ <td class=who>@if{@eq{@who{@id}}{}}
+ {@if{@eq{@state{@id}}{random}}
+ {@label{playing.randomtrack}}
+ { }}
+ {@who{@id}}
+ </td>
+ <td class=artist>@right{play}
+ {<a class=directory
+ href="@url?action=choose&directory=@urlquote{@dirname{@dirname{@track}}}"
+ title="@label{playing.artistverbose}">@part{@id}{short}{artist}</a>}
+ {<span class=directory
+ title="@part{artist}@">@part{short}{artist}</span>}
+ </td>
+ <td class=album>@right{play}
+ {<a class=directory
+ href="@url?action=choose&directory=@urlquote{@dirname{@track}}"
+ title="@label{playing.albumverbose}">@part{short}{album}</a>}
+ {<span class=directory
+ title="@part{album}@">@part{short}{album}</span>}
+ </td>
+ <td class=title><span title="@part{@id}{title}">@part{@id}{short}{title}</span></td>
+ <td class=length>@length{@id}</td>
+ <td class=imgbutton>@if{@removabl{@id}}
+ {<a class=imgbutton
+ href="@url?action=scratch&id=@id@back">
+ <img class=button src="@image{scratch}"
+ title="@label{playing.scratchverbose}"
+ alt="@label{playing.scratch}"></a>}
+ {<img class=button src="@image{noscratch}"
+ title="@label{playing.scratchverbose}"
+ alt="@label{playing.scratch}">}
+ </td>
+ @ifmanage{
<td class=imgbutton> </td>
<td class=imgbutton> </td>
<td class=imgbutton> </td>
<td class=imgbutton> </td>
- }@
+ }{}
</tr>
- }@}@
+ }
@if{@isqueue@}{
<tr class=next>
- <td colspan=@if{@arg{mgmt}}{11}{7}@>@label{playing.next}</td>
+ <td colspan=@ifmanage{11}{7}@>@label{playing.next}</td>
</tr>
+ }
@queue{
<tr class=@parity@>
- <td class=when>@when@</td>
- <td class=who>@if{@eq{@who@}{}@}{@if{@eq{@state@}{random}@}{@label{queue.randomtrack}}{ }@}{@who@}@</td>
+ <td class=when>@when</td>
+ <td class=who>@if{@eq{@who@}{}@}{@if{@eq{@state@}{random}@}{@label{queue.randomtrack}}{ }@}{@who@}</td>
<td class=artist>@right{play}{<a class=directory
- title="@part{artist}@"
- href="@url?action=choose&directory=@urlquote{@dirname{@dirname{@part:path@}@}@}@"
- >@part{short}{artist}@</a>}{<span class=directory
- title="@part{artist}@"
- >@part{short}{artist}@</span>}@</td>
+ title="@part{@id}{artist}@"
+ href="@url?action=choose&directory=@urlquote{@dirname{@dirname{@track}@}@}@"
+ >@part{@id}{short}{artist}</a>}{<span class=directory
+ title="@part{@id}{artist}@"
+ >@part{@id}{short}{artist}</span>}</td>
<td class=album>@right{play}{<a class=directory
- title="@part{album}@"
- href="@url?action=choose&directory=@urlquote{@dirname{@part:path@}@}@"
- >@part{short}{album}@</a>}{<span class=directory
- title="@part{album}@"
- >@part{short}{album}@}@</td>
+ title="@part{@id}{album}@"
+ href="@url?action=choose&directory=@urlquote{@dirname{@track}@}@"
+ >@part{@id}{short}{album}</a>}{<span class=directory
+ title="@part{@id}{album}@"
+ >@part{@id}{short}{album}@}</td>
<td class=title><span
- title="@part{title}@">@part{short}{title}@</span></td>
- <td class=length>@length@</td>
+ title="@part{@id}{title}@">@part{@id}{short}{title}</span></td>
+ <td class=length>@length</td>
<td class=imgbutton>@if{@removable@}{<a class=imgbutton
- href="@url?action=remove&id=@id@&mgmt=@arg{mgmt}"><img
+ href="@url?action=remove&id=@id@@back"><img
class=button src="@image{scratch}"
title="@label{playing.removeverbose}"
alt="@label{playing.remove}"></a>}{<img
class=button src="@image{noscratch}"
title="@label{playing.removeverbose}"
- alt="@label{playing.remove}">}@</td>
+ alt="@label{playing.remove}">}</td>
- @if{@arg{mgmt}}{
+ @if{@eq{@arg{action}}{manage}}{
@if{@or{@isfirst@}
{@not{@movable@}@}@}{
<!-- cannot move up -->
<!-- can move up -->
<td class=imgbutton>
<a class=imgbutton
- href="@url?action=move&id=@id@&delta=2147483647&mgmt=true"><img
+ href="@url?action=move&id=@id@&delta=2147483647@back"><img
class=button src="@image{upall}"
title="@label{playing.upallverbose}"
alt="@label{playing.upall}"></a>
<td class=imgbutton>
<a class=imgbutton
- href="@url?action=move&id=@id@&delta=1&mgmt=true"><img
+ href="@url?action=move&id=@id@&delta=1@back"><img
class=button src="@image{up}"
title="@label{playing.upverbose}" alt="@label{playing.up}"></a>
- }@
+ }
@if{@or{@islast@}
{@not{@movable@}@}@}{
<!-- can move down -->
<td class=imgbutton>
<a class=imgbutton
- href="@url?action=move&id=@id@&delta=-2147483647&mgmt=true"><img
+ href="@url?action=move&id=@id@&delta=-2147483647@back"><img
class=button src="@image{downall}"
title="@label{playing.downallverbose}"
alt="@label{playing.downall}"></a>
<td class=imgbutton>
<a class=imgbutton
- href="@url?action=move&id=@id@&delta=-1&mgmt=true"><img
+ href="@url?action=move&id=@id@&delta=-1@back"><img
class=button src="@image{down}"
title="@label{playing.downverbose}" alt="@label{playing.down}"></a>
- }@
-
- }@
+ }
+ }
</tr>
- }@}@
+ }
</table>
-}@
+}
-@include{topbarend}@
+@include{topbarend}@#
</body>
</html>
-@@
-<!--
+@discard{
Local variables:
mode:sgml
sgml-always-quote-attributes:nil
sgml-indent-step:1
sgml-indent-data:t
End:
--->
+}@
-@include:stylesheet@
-@@
-Anything that goes in all html HEAD elements goes here.
-<!--
+@discard{
This file is part of DisOrder.
-Copyright (C) 2004 Richard Kettlewell
+Copyright (C) 2004, 2008 Richard Kettlewell
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
--->
+
+Anything that goes in all html HEAD elements goes here.
+}@#
+@include{stylesheet.tmpl}@#
- <link rel=stylesheet type="text/css" href="@image:disorder.css@">
-@@
-This file is a standard place to put a link to a stylesheet,
-or an embedded stylesheet.
-<!--
+@discard{
This file is part of DisOrder.
Copyright (C) 2005, 2007, 2008 Richard Kettlewell
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA
--->
+
+This file is a standard place to put a link to a stylesheet,
+or an embedded stylesheet.
+}@#
+ <link rel=stylesheet
+ type="text/css"
+ href="@image{disorder.css}">