wmget: Add version 0.6.0 to repository.

Obtained from [1].

[1] http://amtrickey.net/download/wmget-0.6.0-src.tar.gz
This commit is contained in:
Doug Torrance 2016-02-07 00:53:15 -05:00 committed by Carlos R. Mafra
parent 9c71958173
commit 6efd8b9b4a
26 changed files with 5326 additions and 0 deletions

179
wmget/Makefile Normal file
View file

@ -0,0 +1,179 @@
# Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Makefile for the ``wmget'' (formerly ``wmcurl'') project.
# This Makefile requires GNU make and probably other GNU stuff...
########################################################################
# Build Targets:
#
# all [default]: Builds the wmget application and documentation
# install: Installs the application
# uninstall: Attempts to uninstall, if this makefile installed it
# dockapplib: Recurses into the dockapp dir and builds the library
# doc: Builds all documentation (okay, it's only a manpage)
# clean: Cleans, except for generated HTML/man docs
# docclean: Cleans generated docs
# slackpkg: Builds a Slackware package in packages/slackware
# sourceball: Builds a source+docs tarball in packages/source
########################################################################
all: wmget doc
.PHONY: all install uninstall dockapplib doc clean \
docclean slackpkg sourceball
##### BUILD SETTINGS AND VARIABLES #####################################
# To specify a different prefix, you can override this on the command line
# make PREFIX=/opt/dockapps install
PREFIX= /usr/local
INSTALLDIR= install -d -m 755
INSTALLBIN= install -m 555
INSTALLMAN= install -m 444
CC= gcc
CFLAGS= -Wall -W -I/usr/X11R6/include -O
# The following line is what I use during development
#CFLAGS:= $(CFLAGS) -Werror -g
LDFLAGS= -L/usr/X11R6/lib -lXpm -lXext -lX11 -lm -lcurl
DOCS= wmget.1
VERSION:= $(shell grep '\#define WMGET_VERSION ' wmget.h \
| sed -e 's/.*"\(.*\)".*/\1/' )
OBJS= server.o \
request.o \
cancel.o \
list.o \
retrieve.o \
iq.o \
wmget.o \
configure.o \
messages.o \
usage.o
DALIBDIR= dockapp
DALIB= $(DALIBDIR)/dockapp.a
ALL_SRCS= $(subst .o,.c,$(OBJS))
##### PROGRAM ##########################################################
install: all
echo $(PREFIX) > install.prefix ; \
$(INSTALLDIR) $(PREFIX)/bin ; \
$(INSTALLBIN) wmget $(PREFIX)/bin/wmget ; \
$(INSTALLDIR) $(PREFIX)/man/man1 ; \
$(INSTALLMAN) wmget.1 $(PREFIX)/man/man1/wmget.1 ; \
uninstall:
cd `cat install.prefix` && rm -f bin/wmget man/man1/wmget.1
rm -f install.prefix
wmget: dockapplib $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(DALIB) -o $@
##### LIBRARY ##########################################################
dockapplib:
make -C $(DALIBDIR)
##### DOCUMENTATION ####################################################
doc: $(DOCS)
# NOTE: The wmget refentry page uses an <?xml-stylesheet?> PI, I use an XML
# catalog file to map the given URL to a local path
wmget.1: wmget.refentry.xml
xsltproc --nonet $<
##### CLEANUP ##########################################################
clean:
rm -f *.o wmget core pod2html-* install.prefix \
wmget.html
make -C dockapp clean
docclean:
rm -f $(DOCS)
##### SLACKWARE PACKAGE ################################################
SLACK_PFX= slackroot/$(PREFIX)
SLACK_PACKAGE= packages/slackware/wmget-$(VERSION).tgz
slackpkg: wmget doc
-mkdir -p packages/slackware
rm -rf slackroot
$(INSTALLDIR) $(SLACK_PFX)
$(INSTALLDIR) $(SLACK_PFX)/bin
$(INSTALLDIR) $(SLACK_PFX)/man
$(INSTALLDIR) $(SLACK_PFX)/man/man1
$(INSTALLBIN) wmget $(SLACK_PFX)/bin/wmget
$(INSTALLMAN) wmget.1 $(SLACK_PFX)/man/man1/wmget.1
cd slackroot && \
tar czv -f ../$(SLACK_PACKAGE) \
--owner=root \
--group=root \
*
rm -rf slackroot
##### SOURCE PACKAGE ###################################################
SOURCEBALL= packages/source/wmget-$(VERSION)-src.tar.gz
sourceball: doc clean
-mkdir -p packages/source
cd .. && \
tar czv --exclude RCS \
--exclude .\* \
--exclude packages \
--exclude tags \
--exclude \*~ \
--exclude working \
--exclude www \
-f wmget/$(SOURCEBALL) \
wmget
##### WEB SITE #########################################################
WWW_SRC= $(HOME)/amtrickey.net/src
WWW_DOWNLOAD= $(WWW_SRC)/download
WWW_WMGET= $(WWW_SRC)/wmget
www: slackpkg sourceball
cp $(SLACK_PACKAGE) $(WWW_DOWNLOAD)
cp $(SOURCEBALL) $(WWW_DOWNLOAD)
cp NEWS wmget.refentry.xml $(WWW_WMGET)

89
wmget/NEWS Normal file
View file

@ -0,0 +1,89 @@
Development History for wmget
wmget 0.6.0 - More options, nominal error handling, fixes
* New options: auth, proxy_auth, ascii, interface, referer, headers
* Failed downloads now generate a <filename>.ERROR file containing
error text, instead of making you guess
* Minor fixes & cleanups
* Rewrote much of the manpage, and moved it from POD to DocBook-XML
wmget 0.5.0 - RC file, more options, bugfixes
* wmget may now be configured via ~/.wmgetrc
* Cleaned up and more comprehensive command-line options
* More download options available: proxy support, redirect support,
and yes, FINALLY... output directory support!
* Some old source cleanups, and some new source sloppiness.
* Fix to a bug that created garbage download names
wmget 0.4.4 - Fix to work with libcurl > 7.9.5
* Thanks again to Rafal Zawadzki for a report: wmget + newer libcurl
did not show download progress. After updating my libcurl it
turns out that the progress callback function signature has
changed, so a little #iffing and all is well again.
wmget 0.4.3 - Minor bugfixes, cleanups
* Fixed the makefile sourceball target to unpack into a subdirectory
* Cleaned up a couple of bits of code
* Removed -Werror and -g from normal builds
* Thanks to Rafal Zawadzki for building a .deb and submitting it to
Debian Sid, and to Kurt Hindenburg for reporting build and tarball
issues.
wmget 0.4.2 - Fixed AfterStep support
* The new dockapp code no longer worked right under AfterStep's
Wharf. Fixed. Thanks to Andy Jalics for the email bugreport.
* Updated the docs to reflect the fact that Window Maker is not the
only WM out there. :)
wmget 0.4.1 - Bugfix
* Off-by-one error in a memmove() caused garbage characters to
appear at the end of downloaded filenames on the local system in
certain cases.
wmget 0.4.0 - New ``dockapp'' library, cleanups, features
* Middle clicking on the dockapp now starts a download of the URI in
the primary X selection (right-click on a link in Mozilla, select
``Copy Link Address'', and middle-click on wmget... presto.)
* Wrote from scratch a new little library to do dockapp programming.
Hides all the X stuff, and provides a main loop with a few new
features. Replaces the old wmgeneral.c.
* Fixed default save-name of URL's with no trailing filename
component---now selects the final path component, whatever it is
(e.g. http://example.com/ => "example.com"); also fixed the
display name to always show the basename of the save filename if
no display name was provided
* Added a Slackware package target.
* Corrected/added command line help (LIST was missing...)
* Cleanups: Makefile, source, docs.
wmget 0.3.0 - A couple of new features, cleanups, fixes
* Implemented CANCEL and LIST commands
* Minor doc cleanup
* Fixed it to work with more recent libcurl's (thanks to Paul Tweedy
for emailing me about that; I hadn't upgraded my cURL in a while)
wmget 0.2.0 - Rewrite of the client/server ipc. Renaming.
Very little user-visible change.
Rewrote client/server ipc to use text requests/responses over a unix
domain socket rather than binary structures over a FIFO.
Also, with this version, I renamed it from ``wmcurl'' to ``wmget''.
The old name was sorta a poor choice: while I still rely on, and want
to give full credit to, the cURL guys, the name ``wmcurl'' is
not very intuitive. Plus I didn't want people thinking it was part
of the cURL project proper. Plus this saves me a keystroke when
typing it :)
wmcurl 0.1.0 - Initial release.

70
wmget/README Normal file
View file

@ -0,0 +1,70 @@
wmget - Background download manager in a Window Maker dock app
Copyright (C) 2001-2003 Aaron Trickey
Free software, licensed under ``MIT-style'' terms (see below)
ABSOLUTELY NO WARRANTY -- SEE BELOW
[This file is only rarely updated. If you're upgrading from a prior
release, please see the file NEWS.]
wmget is a dock app for the GNU Window Maker window manager (or one of
the many other WM's which support dockapps) which makes it more
convenient to perform long downloads in the background. It uses the
excellent libcurl library, part of the cURL automated-download program,
to perform file retrieval.
The latest information about and version of wmget will hopefully always
be available at:
<http://amtrickey.net/wmget/index.html>
Wmget requires libcurl (various versions from 7.5.1 on were used during
development); this is part of the main cURL distribution. Grab it from:
<http://curl.haxx.se/>
configure and install it if you don't have it. And for routine download
tasks, check out their ``curl'' command-line tool.
wmget is written entirely in C. It (as of 0.4) uses a from-scratch
dockapp library I cooked up for fun. This replaced a common dockapp
utility library named wmgeneral.c, which helped me get off the ground;
thanks to the authors (Martijn Pieterse & Dave Clark).
The wmget manpage is written in Perl's POD documentation language. If you
don't have Perl, that's fine; just don't delete and try to remake the
manpage I provided.
I'm releasing my code under the same licensing terms as the X Window
System. These terms are commonly known as ``MIT'' licensing terms,
since that's where they originated. I've attached these licensing
terms, and the important non-warranty clauses, below.
Enjoy!
Aaron Trickey
aaron at amtrickey dot net
----------------------------------------------------------------------------
Licensing and Non-Warranty Terms and Disclaimers.
Copyright (c) 2001-2003 Aaron Trickey
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

14
wmget/TODO Normal file
View file

@ -0,0 +1,14 @@
Missing things:
- graphical indication of errors
Improvable things:
- more packages
- XDND support
- bigger clickable region
- allow overwrite of zero-length files and/or clean up files after
failure

93
wmget/cancel.c Normal file
View file

@ -0,0 +1,93 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
cancel.c - Client code for canceling download requests
When invoked with the ``cancel'' argument, main() calls cancel(),
defined below. This code expects a job ID and forms and submits
a CANCEL request to the server.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wmget.h"
int cancel (int argc, char **argv)
{
char line[MAXCMDLEN + 1];
char *word_break;
FILE *fp;
job_id_t job_id;
/* The argument vector we receive is guaranteed to start with
* { <program-name>, cancel }. Currently, we don't support any
* options or anything; we just expect a single nonzero job ID.
*/
if (argc < 3) {
error ("What do you want me to cancel? (Missing job number)");
return 1;
} else if (argc > 3) {
error ("Extra arguments: cancel takes only a job number");
return 1;
} else if (!(job_id = strtoul (argv[2], &word_break, 0))
|| *word_break) {
error ("Invalid job number (must be an integer > 0)");
return 1;
}
if (!(fp = iq_client_connect ()))
return 1;
if (fprintf (fp, "CANCEL JOBID(%lu)\r\n", job_id) == EOF) {
error_sys ("Could not submit command to server");
fclose (fp);
return 1;
}
if (!fgets (line, sizeof line - 1, fp)) {
error ("Server did not respond to command!");
fclose (fp);
return 1;
}
/* Extract the first word and compare. */
word_break = line + strcspn (line, " \t\r\n");
if (*word_break)
*word_break++ = 0;
if (strcasecmp (line, RESPONSE_JOB_CANCELED)) {
error ("Server responded with error: %s", word_break);
fclose (fp);
return 1;
}
fclose (fp);
return 0;
}

62
wmget/config.def Normal file
View file

@ -0,0 +1,62 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
config.def - Definition of the command line and RC file settings
This file is #included in multiple places in the source code, each
of which interprets it by providing a different #define for the
macros used here. Each macro takes the following parameters:
O(
'<char|\0>', <-- short opt character, \0 for none
name, <-- option name (*)
yes|no, <-- has argument?
"documentation" <-- provided in help
)
(*): These are composed to produce RC file keywords, long
command-line options, and internal C identifiers. Write them as C
identifiers (they'll be prefixed, don't worry about collision with
keywords or other symbols), not as quoted strings.
*/
O( 's', silent, no, "Suppress any non-error messages" )
O( 'V', verbose, no, "Produce verbose output (debugging only)" )
O( 'o', output, yes,"Where to save the downloaded file" )
O( 'd', display, yes,"The text to display in the progress bar" )
O( 'O', overwrite, no, "Allow overwriting an existing file?" )
O( 'C', continue, no, "Continue a previously-aborted download" )
O( 'a', auth, yes,"USERNAME:PASSWORD for server" )
O( 'p', proxy, yes,"Specify an HTTP proxy server" )
O( 'P', proxy_auth, yes,"USERNAME:PASSWORD for HTTP proxy" )
O( 'f', follow, yes,"How many HTTP redirects to follow (0 = off)" )
O( 'U', user_agent, yes,"User-Agent to provide to Web servers" )
O( 'B', ascii, no, "Force FTP downloads to be ASCII-mode" )
O( 'e', referer, yes,"Set the referer URL passed to the HTTP server")
O( 'n', interface, yes,"Use this network interface (e.g. eth0)" )
O( 'h', headers, no, "Include the HTTP response header in the file" )
/* vim: set filetype=c: */

419
wmget/configure.c Normal file
View file

@ -0,0 +1,419 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
config.c - Implementation of command-line and RC-file configuration
The code in this file parses RC files and command lines and provides
defaults for what you don't specify. Used to configure both
requests and servers.
*/
#include <string.h>
#include <malloc.h>
#include <ctype.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wmget.h"
/* Option characters.
*/
#define O(s,l,a,t) optchar_##l = s,
enum {
#include "config.def"
};
#undef O
/***********************************************************************
* clear_request(): Initialize an empty Request object.
*/
void clear_request (Request *req)
{
req->source_url = 0;
req->display = 0;
req->save_to = 0;
req->overwrite = -1;
req->continue_from = -1;
req->proxy = 0;
req->follow = -1;
req->user_agent = 0;
req->use_ascii = -1;
req->referer = 0;
req->include = -1;
req->interface = 0;
req->proxy_auth = 0;
req->auth = 0;
}
static void set_silent ()
{
set_output_level (OL_SILENT);
}
static void set_verbose ()
{
set_output_level (OL_DEBUG);
}
static void set_output (Request *r, ServerConfig *c, const char *value)
{
if (r) r->save_to = value;
if (c) {
struct stat st;
/* For server configuration, we must set the default to an
* absolute path.
*/
if (value[0] == '/') {
if (strlen (value) + 1 > sizeof c->job_defaults.save_to) {
error (
"Download directory name too long! Defaulting to home directory");
strcpy (c->job_defaults.save_to, home_directory ());
} else {
strcpy (c->job_defaults.save_to, value);
}
} else {
if (strlen (value) + strlen (home_directory ()) + 2
> sizeof c->job_defaults.save_to) {
error (
"Download directory name too long! Defaulting to home directory");
strcpy (c->job_defaults.save_to, home_directory ());
} else {
strcpy (c->job_defaults.save_to, home_directory ());
strcat (c->job_defaults.save_to, "/");
strcat (c->job_defaults.save_to, value);
}
}
/* And we need to make sure it's really a directory.
*/
if ( stat (c->job_defaults.save_to, &st)
|| !S_ISDIR (st.st_mode)) {
error (
"``output'' option is not a valid directory! Defaulting to home");
strcpy (c->job_defaults.save_to, home_directory ());
}
}
}
static void set_display (Request *r, ServerConfig *c, const char *value)
{
if (r) r->display = value;
if (c) info ("Cannot set ``display'' option on dockapp");
}
static void set_overwrite (Request *r, ServerConfig *c)
{
if (r) r->overwrite = 1;
if (c) c->job_defaults.overwrite = 1;
}
static void set_continue (Request *r, ServerConfig *c)
{
if (r) r->continue_from = 1;
if (c) c->job_defaults.continue_from = 1;
}
static void set_proxy (Request *r, ServerConfig *c, const char *value)
{
if (r) r->proxy = value;
if (c) STRCPY_TO_ARRAY (c->job_defaults.proxy, value);
}
static void set_follow (Request *r, ServerConfig *c, const char *value)
{
if (r) r->follow = atoi (value);
if (c) c->job_defaults.follow = atoi (value);
}
static void set_user_agent (Request *r, ServerConfig *c, const char *v)
{
if (r) r->user_agent = v;
if (c) STRCPY_TO_ARRAY (c->job_defaults.user_agent, v);
}
static void set_ascii (Request *r, ServerConfig *c)
{
if (r) r->use_ascii = 1;
if (c) c->job_defaults.use_ascii = 1;
}
static void set_referer (Request *r, ServerConfig *c, const char *v)
{
if (r) r->referer = v;
if (c) STRCPY_TO_ARRAY (c->job_defaults.referer, v);
}
static void set_headers (Request *r, ServerConfig *c)
{
if (r) r->include = 1;
if (c) c->job_defaults.include = 1;
}
static void set_interface (Request *r, ServerConfig *c, const char *v)
{
if (r) r->interface = v;
if (c) STRCPY_TO_ARRAY (c->job_defaults.interface, v);
}
static void set_proxy_auth (Request *r, ServerConfig *c, const char *v)
{
if (r) r->proxy_auth = v;
if (c) STRCPY_TO_ARRAY (c->job_defaults.proxy_auth, v);
}
static void set_auth (Request *r, ServerConfig *c, const char *v)
{
if (r) r->auth = v;
if (c) STRCPY_TO_ARRAY (c->job_defaults.auth, v);
}
/***********************************************************************
* load_cmdline(): Parse options from the command line.
*/
static int load_cmdline (int argc, char **argv,
Request *req, ServerConfig *cfg)
{
int o;
static const char shortopts[] = {
#define no
#define yes , ':'
#define O(s,l,a,t) s a,
#include "config.def"
#undef O
#undef no
#undef yes
0
};
static const struct option longopts[] = {
#define no no_argument
#define yes required_argument
#define O(s,l,a,t) { #l, a, 0, s },
#include "config.def"
#undef O
#undef yes
#undef no
{ 0, 0, 0, 0 }
};
while ((o = getopt_long (argc, argv, shortopts, longopts, 0))
!= EOF) {
switch (o) {
default:
return 1;
#define yes , optarg
#define no
#define O(s,l,a,t) \
case optchar_##l: \
set_##l (req, cfg a); \
break;
#include "config.def"
#undef O
#undef no
#undef yes
}
}
if (optind < argc) {
if (req) {
if (strlen (argv[optind]) > MAXURL) {
error ("URL too long!");
} else {
req->source_url = argv[optind];
}
++optind;
}
if (cfg) {
if (strcasecmp (argv[optind], "dock") == 0) {
/* That's part of the syntax... ignore. */
++optind;
}
}
}
if (optind < argc) {
error ("Extra argument: '%s'", argv[optind]);
}
return 0;
}
static void read_rcfile (FILE *rcfp, ServerConfig *cfg)
{
char line[MAXRCLINELEN];
while (fgets (line, sizeof line, rcfp)) {
char *name = 0;
char *value = 0;
char *value_end = 0;
char *tictactoe = strchr (line, '#');
if (tictactoe) {
*tictactoe = '\0';
}
name = line; /* locate name: */
while (*name && isspace (*name)) /* skip leading ws */
++name;
if (!*name) { /* no name? skip line */
continue;
}
value = name; /* locate value: */
while (*value && !isspace (*value)) /* skip name */
++value;
if (*value) { /* not eol: look for val */
*value++ = '\0'; /* terminate name */
while (*value && isspace (*value)) /* skip dividing ws */
++value;
value_end = value + strlen (value); /* right-trim */
--value_end;
while (value_end > value && isspace (*value_end)) {
*value_end-- = 0;
}
}
# define ARG_yes(NAM) \
if (!*value) { \
error ("Keyword '" #NAM "' in config file is missing " \
"its required argument"); \
} else { \
debug ("set " #NAM " (%s)", value); \
set_##NAM (0, cfg, value); \
}
# define ARG_no(NAM) \
if (*value) { \
error ("Keyword '" #NAM "' in config file has an "\
"extra argument: '%s'", value); \
} else { \
debug ("set " #NAM " <no value>"); \
set_##NAM (0, cfg); \
}
# define O(s,l,a,t) \
if (strcasecmp (name, #l) == 0) { \
ARG_##a (l) \
} else
# include "config.def"
# undef O
# undef ARG_yes
# undef ARG_no
error ("Unknown keyword in config file: %s", name);
}
}
static void load_rcfile (ServerConfig *cfg)
{
char rcfile[MAXPATHLEN + 1];
static const char *rcfile_base = "/.wmgetrc";
FILE *rcfp = 0;
strcpy (rcfile, home_directory ());
if (strlen (rcfile) + strlen (rcfile_base) >= sizeof rcfile) {
error ("Your home directory name is too long!");
return;
}
strcat (rcfile, rcfile_base);
if ((rcfp = fopen (rcfile, "rt"))) {
read_rcfile (rcfp, cfg);
} else {
/* rcfiles are fully optional... */
debug_sys ("Could not open rcfile '%s'", rcfile);
}
}
void config_server (int argc, char **argv, ServerConfig *cfg)
{
/* Default job options: These take effect unless overridden by
* server configuration or per-job configuration.
*/
cfg->job_defaults.display[0] = 0;
STRCPY_TO_ARRAY (cfg->job_defaults.save_to, home_directory ());
cfg->job_defaults.overwrite = 0;
cfg->job_defaults.continue_from = 0;
cfg->job_defaults.proxy[0] = 0;
cfg->job_defaults.follow = 5;
STRCPY_TO_ARRAY (cfg->job_defaults.user_agent, DEFAULT_USER_AGENT);
cfg->job_defaults.use_ascii = 0;
cfg->job_defaults.referer[0] = 0;
cfg->job_defaults.include = 0;
cfg->job_defaults.interface[0] = 0;
cfg->job_defaults.proxy_auth[0] = 0;
cfg->job_defaults.auth[0] = 0;
load_rcfile (cfg);
load_cmdline (argc, argv, 0, cfg);
}
void config_request (int argc, char **argv, Request *req)
{
clear_request (req);
load_cmdline (argc, argv, req, 0);
}

35
wmget/dockapp/Makefile Normal file
View file

@ -0,0 +1,35 @@
# Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Makefile for the dockapp library packaged in the ``wmget'' application.
DAOBJS= da_run.o da_x.o da_mouse.o
CFLAGS= -ansi -Wall -W -Werror -g
LDFLAGS= -L/usr/X11R6/lib -lXpm -lXext -lX11
dockapp.a: $(DAOBJS)
ar rcsv dockapp.a $(DAOBJS)
unittest: unittest.o $(DAOBJS)
clean:
rm -f unittest.o unittest dockapp.a $(DAOBJS)

125
wmget/dockapp/da_mouse.c Normal file
View file

@ -0,0 +1,125 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
dockapp/da_mouse.c - Mouse/clickregion handling
This code keeps track of app-registered ``clickregions'', which
represent clickable rectangles and button/modifier masks associated
with app callbacks.
*/
#include <stdio.h>
#include <sys/types.h>
#include "dockapp.h"
#define DOCKAPP_EXPOSE_INTERNALS
#include "da_mouse.h"
typedef struct {
int x, y, w, h;
int buttonmask;
dockapp_rv_t (*cb) (void *, int x, int y);
void *cbdata;
} da_clickregion;
/* for now we use a simple array of clickregions
*/
#define DA_MAX_CLICKREGIONS 50
static da_clickregion da_clickregions[DA_MAX_CLICKREGIONS];
static size_t da_num_clickregions = 0;
/* a mouse-down simply stores the mouse state here for later processing
* by a mouse-up
*/
static int da_mouse_down_x;
static int da_mouse_down_y;
static int da_mouse_down_buttons;
void da_mouse_button_down (int x, int y, int buttons)
{
/* fprintf (stderr, "hello from da_mouse_button_down\n"); */
da_mouse_down_x = x;
da_mouse_down_y = y;
da_mouse_down_buttons = buttons;
}
dockapp_rv_t da_mouse_button_up (int x, int y, int buttons)
{
da_clickregion *c, *end;
/*
fprintf (stderr, "hello from da_mouse_button_up, btns=0x%02x\n",
buttons);
*/
end = da_clickregions + da_num_clickregions;
for (c = da_clickregions; c < end; ++c) {
if (((buttons & c->buttonmask) == c->buttonmask)
&& x >= c->x && x < c->x + c->w
&& y >= c->y && y < c->y + c->h
&& da_mouse_down_x >= c->x && da_mouse_down_x < c->x + c->w
&& da_mouse_down_y >= c->y && da_mouse_down_y < c->y + c->h) {
if (c->cb (c->cbdata, x, y) == dockapp_exit)
return dockapp_exit;
}
}
return dockapp_ok;
}
dockapp_rv_t dockapp_add_clickregion (
int x, int y, int w, int h,
int buttonmask,
dockapp_rv_t (*cb) (void *, int x, int y),
void *cbdata)
{
da_clickregion *c;
if (da_num_clickregions >= DA_MAX_CLICKREGIONS)
return dockapp_rv_too_many_clickregions;
c = da_clickregions + da_num_clickregions++;
c->x = x;
c->y = y;
c->w = w;
c->h = h;
c->buttonmask = buttonmask;
c->cb = cb;
c->cbdata = cbdata;
return dockapp_ok;
}

47
wmget/dockapp/da_mouse.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef I_DOCKAPP_DA_MOUSE_H
#define I_DOCKAPP_DA_MOUSE_H
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
dockapp/da_mouse.h - Mouse/clickregion handling defs
Private header file.
*/
#ifndef DOCKAPP_EXPOSE_INTERNALS
# error da_mouse.h #included... this is internal to the dockapp lib...
#endif
void da_mouse_button_down (
int xpos,
int ypos,
int state);
dockapp_rv_t da_mouse_button_up (
int xpos,
int ypos,
int state);
#endif /* I_DOCKAPP_DA_MOUSE_H */

266
wmget/dockapp/da_run.c Normal file
View file

@ -0,0 +1,266 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
dockapp/da_run.c - Main dockapp loop; fd polling; periodic callback
This file implements dockapp_run(), which provides the main loop of
the dockapp application. In addition, it implements
dockapp_add_pollfd(), which adds polling of arbitrary file
descriptors to the main loop, and dockapp_set_periodic_callback(),
which sets up a periodic app callback.
*/
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/poll.h>
#include "dockapp.h"
/* we keep two parallel structures... one, the list of pollfd's for
* poll(2)'s benefit, and the other, the collection of callback
* information
*/
typedef struct {
dockapp_rv_t (*cb) (void *cbdata, short revents);
void *cbdata;
short revents;
} da_pollcb;
enum {
da_max_pollfds = 50
};
static struct pollfd da_pollfds[da_max_pollfds];
static da_pollcb da_pollcbs[da_max_pollfds];
static int da_num_pollfds = 0;
dockapp_rv_t dockapp_add_pollfd (
int fd,
short pollevents,
dockapp_rv_t (*cb) (void *, short pollstatus),
void *cbdata)
{
struct pollfd *pfd;
da_pollcb *pcb;
assert (da_num_pollfds < da_max_pollfds);
/*
fprintf (stderr, "adding pollfd #%d, fd = %d, events = %hu\n",
da_num_pollfds, fd, pollevents);
*/
pfd = da_pollfds + da_num_pollfds;
pcb = da_pollcbs + da_num_pollfds;
pfd->fd = fd;
pfd->events = pollevents;
pcb->cb = cb;
pcb->cbdata = cbdata;
++da_num_pollfds;
return dockapp_ok;
}
dockapp_rv_t dockapp_remove_pollfd (
int fd)
{
int i = da_num_pollfds;
struct pollfd *pfd = da_pollfds;
da_pollcb *pcb = da_pollcbs;
for ( ; i; --i, ++pfd, ++pcb) {
if (pfd->fd == fd) {
memmove (pfd, pfd + 1, (i - 1) * sizeof (struct pollfd));
memmove (pcb, pcb + 1, (i - 1) * sizeof (da_pollcb));
return dockapp_ok;
}
}
/* not found... */
return dockapp_invalid_arg;
}
/* periodic callback info... if da_timer_cb is null, then no callback is
* set.
*/
static dockapp_rv_t (*da_timer_cb) (void *) = 0;
static void *da_timer_cbdata;
static struct timeval da_timer_next_timeout;
static long da_timer_interval_msec;
static void da_reset_timer (void)
{
int rv;
rv = gettimeofday (&da_timer_next_timeout, 0);
assert (rv == 0);
/*
fprintf (stderr,
"da_reset_timer: RIGHT NOW is %ld.%ld, msec = %ld\n",
da_timer_next_timeout.tv_sec,
da_timer_next_timeout.tv_usec,
da_timer_interval_msec);
*/
da_timer_next_timeout.tv_usec
+= (da_timer_interval_msec % 1000L) * 1000L;
da_timer_next_timeout.tv_sec
+= da_timer_interval_msec / 1000L
+ da_timer_next_timeout.tv_usec / 1000000L;
da_timer_next_timeout.tv_usec %= 1000000L;
/*
fprintf (stderr,
"da_reset_timer: NEXT TIMEOUT is %ld.%ld\n",
da_timer_next_timeout.tv_sec,
da_timer_next_timeout.tv_usec);
*/
}
static long da_timer_msec_remaining (void)
{
struct timeval right_now;
int rv;
rv = gettimeofday (&right_now, 0);
return
(da_timer_next_timeout.tv_sec - right_now.tv_sec) * 1000L
+ (da_timer_next_timeout.tv_usec - right_now.tv_usec) / 1000L;
}
void dockapp_set_periodic_callback (
long msec,
dockapp_rv_t (*cb) (void *),
void *cbdata)
{
da_timer_cb = cb;
da_timer_cbdata = cbdata;
da_timer_interval_msec = msec;
da_reset_timer ();
}
void dockapp_remove_periodic_callback (void)
{
da_timer_cb = 0;
da_timer_cbdata = 0;
da_timer_interval_msec = 0;
}
/* this is the main loop for the dockapp...
*/
dockapp_rv_t dockapp_run (void)
{
for ( ; ; ) {
int rv;
int poll_timeout = -1; /* infinite unless periodic callback */
if (da_timer_cb) {
if (da_timer_msec_remaining () <= 0) {
rv = da_timer_cb (da_timer_cbdata);
da_reset_timer ();
}
poll_timeout = da_timer_msec_remaining ();
}
/*
fprintf (stderr, "about to poll(%d fd, %d timeout)\n",
da_num_pollfds, poll_timeout);
*/
rv = poll (da_pollfds, da_num_pollfds, poll_timeout);
/*
fprintf (stderr, "poll returned %d\n", rv);
*/
if (rv == 0) {
/* poll timed out; let's loop back up and let the logic
* prior to the poll() invoke the user-defined periodic
* callback
*/
continue;
} else if (rv < 0) {
/* Disregard EINTR; just means that a signal was caught.
* We'll retry later.
*/
if (errno != EINTR)
perror ("poll()");
} else {
/* poll returned with some nonzero number of events...
* collect the callback structures first, then invoke the
* callbacks, since the callback functions are allowed to
* create and destroy callbacks as well
*/
int i;
da_pollcb callbacks[da_max_pollfds];
da_pollcb *c;
int ncallbacks = 0;
for (i = 0; i < da_num_pollfds && ncallbacks < rv; ++i) {
if (da_pollfds[i].revents) {
callbacks[ncallbacks] = da_pollcbs[i];
callbacks[ncallbacks].revents
= da_pollfds[i].revents;
++ncallbacks;
}
}
for (c = callbacks; ncallbacks; --ncallbacks, ++c) {
if (c->cb (c->cbdata, c->revents) == dockapp_exit) {
fprintf (stderr, "exiting dockapp_run()\n");
return dockapp_ok;
}
}
}
}
}

481
wmget/dockapp/da_x.c Normal file
View file

@ -0,0 +1,481 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
dockapp/da_x.c - General X11 code
This file encapsulates all the X11 interface code, such as drawing,
initialization, and selection handling.
*/
#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <unistd.h>
#include "dockapp.h"
#define DOCKAPP_EXPOSE_INTERNALS
#include "da_mouse.h"
#define SELECTION_MAX 1024
static Display *display;
/* We need to update both the ``icon'' and ``main'' windows
* simultaneously... icon for WindowMaker and main for AfterStep...
*/
static Window icon_win;
static Window main_win;
static Pixmap icon_pixmap;
static GC copy_gc;
static GC xor_gc;
/* request the X selection; when the server responds, we will get it in
* da_xfd_callback.
*/
static void da_request_selection ()
{
Atom prop;
prop = XInternAtom (display, "XSEL_DATA", False);
XConvertSelection (
display,
XA_PRIMARY, /* only use the PRIMARY selection */
XA_STRING, /* we only accept strings (the 90% case) */
prop,
icon_win,
CurrentTime);
}
static dockapp_rv_t (*da_selection_cb) (void *, const char *);
static void *da_selection_cbdata;
static dockapp_rv_t da_receive_selection (XEvent *event)
{
Atom actual_type;
int actual_format;
unsigned long actual_length;
unsigned long remaining;
unsigned char *data;
dockapp_rv_t rv;
if (event->xselection.selection != XA_PRIMARY)
return dockapp_ok;
if (event->xselection.property == None) {
fprintf (stderr, "! selection can't become string !\n");
return dockapp_ok;
}
XGetWindowProperty (
event->xselection.display,
event->xselection.requestor,
event->xselection.property,
0, /* from byte zero... */
SELECTION_MAX, /* to this many bytes */
False, /* do not delete after retrieval */
AnyPropertyType,
&actual_type, /* actual type returned */
&actual_format, /* actual format returned */
&actual_length, /* in 8, 16, or 32 bit units */
&remaining, /* unread content (always in 8 bit units!) */
&data); /* property content */
/* We do not support non-string data or incremental transfer. */
if (actual_type != XA_STRING || actual_format != 8) {
fprintf (stderr, "! selection unavailable or not of known type !\n");
if (actual_length) {
XFree (data);
}
return dockapp_ok;
}
rv = da_selection_cb (da_selection_cbdata, (const char *)data);
XFree (data);
return rv;
}
dockapp_rv_t dockapp_request_selection_string (
dockapp_rv_t (*cb) (void *, const char *),
void *cbdata)
{
da_selection_cb = cb;
da_selection_cbdata = cbdata;
da_request_selection ();
return dockapp_ok;
}
static void da_redraw_icon (void)
{
XEvent event;
/* slurp up any queued Expose events */
while (XCheckTypedWindowEvent (
display,
event.xany.window,
Expose,
&event))
;
XCopyArea (
display,
icon_pixmap,
icon_win,
copy_gc,
0, 0, 64, 64, /* source coords */
0, 0); /* dest coords */
XCopyArea (
display,
icon_pixmap,
main_win,
copy_gc,
0, 0, 64, 64, /* source coords */
0, 0); /* dest coords */
}
/* this is the callback bound to the X server socket descriptor
*/
static dockapp_rv_t da_xfd_callback (
void *unused_cbdata,
short unused_revents)
{
(void)unused_cbdata;
(void)unused_revents;
XSync (display, False);
while (XPending (display)) {
XEvent event;
XNextEvent (display, &event);
switch (event.type) {
case ButtonPress:
da_mouse_button_down (
event.xbutton.x,
event.xbutton.y,
event.xbutton.state);
break;
case ButtonRelease:
/* da_mouse_button_up() returns the callback rv of the
* callback which is invoked, if any
*/
return da_mouse_button_up (
event.xbutton.x,
event.xbutton.y,
event.xbutton.state);
break;
case Expose:
da_redraw_icon ();
break;
case DestroyNotify:
XCloseDisplay (display);
return dockapp_exit;
case SelectionNotify:
return da_receive_selection (&event);
}
}
return dockapp_ok;
}
int da_x_error_handler (Display *display, XErrorEvent *xerr)
{
char msgbuf[1024];
XGetErrorText (display, xerr->error_code, msgbuf, sizeof msgbuf);
fprintf (stderr, "X11 error: %s\n", msgbuf);
return 0;
}
/* the following code is stolen largely from wmgeneral.c...
*/
static Pixmap da_create_shaping_bitmap (char **xpm)
{
int i,j,k;
int width, height, numcol, depth;
unsigned long zero=0;
unsigned char bwrite;
int bcount;
unsigned long curpixel;
char xbm_data[64*64];
char *xbm = xbm_data;
sscanf(*xpm, "%d %d %d %d", &width, &height, &numcol, &depth);
for (k=0; k!=depth; k++)
{
zero <<=8;
zero |= xpm[1][k];
}
for (i=numcol+1; i < numcol+64+1; i++) {
bcount = 0;
bwrite = 0;
for (j=0; j<64*depth; j+=depth) {
bwrite >>= 1;
curpixel=0;
for (k=0; k!=depth; k++)
{
curpixel <<=8;
curpixel |= xpm[i][j+k];
}
if ( curpixel != zero ) {
bwrite += 128;
}
bcount++;
if (bcount == 8) {
*xbm = bwrite;
xbm++;
bcount = 0;
bwrite = 0;
}
}
}
return XCreateBitmapFromData (
display,
icon_win,
xbm_data,
64, 64);
}
dockapp_rv_t dockapp_init_gui (
char *appname,
char *argv[],
char **xpmdata)
{
int screen;
Window root;
XpmAttributes xpm_att;
Pixmap xpm_mask;
unsigned long black, white;
(void)argv; /* not currently parsed */
/* initialize x error handler */
XSetErrorHandler (da_x_error_handler);
/* get some stuff out */
display = XOpenDisplay (0);
screen = DefaultScreen (display);
root = RootWindow (display, screen);
black = BlackPixel (display, screen);
white = WhitePixel (display, screen);
/* construct the pixmap */
xpm_att.valuemask = XpmReturnPixels | XpmReturnExtensions;
XpmCreatePixmapFromData (
display,
root,
xpmdata,
&icon_pixmap,
&xpm_mask,
&xpm_att);
/* construct the windows */
main_win = XCreateSimpleWindow (
display,
root, /* parent */
0, 0, 64, 64, /* pos & size */
1, /* border width */
black, /* foreground */
white); /* background */
icon_win = XCreateSimpleWindow (
display,
main_win, /* parent */
0, 0, 64, 64, /* pos & size */
1, /* border width */
black, /* foreground */
white); /* background */
/* request all interesting X events */
XSelectInput (display, main_win,
ButtonPressMask | ExposureMask | ButtonReleaseMask |
PointerMotionMask | StructureNotifyMask);
XSelectInput (display, icon_win,
ButtonPressMask | ExposureMask | ButtonReleaseMask |
PointerMotionMask | StructureNotifyMask);
/* apply the shaping bitmap */
XShapeCombineMask (
display,
icon_win,
ShapeBounding,
0, 0,
da_create_shaping_bitmap (xpmdata),
ShapeSet);
XShapeCombineMask (
display,
main_win,
ShapeBounding,
0, 0,
da_create_shaping_bitmap (xpmdata),
ShapeSet);
/* set wm hints and title */
{
XClassHint classhint;
XWMHints wmhints;
classhint.res_name = appname;
classhint.res_class = appname;
XSetClassHint (display, main_win, &classhint);
wmhints.initial_state = WithdrawnState;
wmhints.icon_window = icon_win;
wmhints.window_group = main_win;
wmhints.flags = StateHint | IconWindowHint | WindowGroupHint;
XSetWMHints (display, main_win, &wmhints);
XStoreName (display, main_win, appname);
}
/* now we need two gc's to do drawing & copying... one which paints
* by overwriting pixels, and one which paints by xor'ing pixels
*/
{
XGCValues gcv;
gcv.graphics_exposures = 0;
gcv.function = GXcopy;
copy_gc = XCreateGC (
display,
root,
GCGraphicsExposures | GCFunction,
&gcv);
gcv.function = GXxor;
xor_gc = XCreateGC (
display,
root,
GCGraphicsExposures | GCFunction,
&gcv);
}
/* finally, show the window */
XMapWindow (display, main_win);
/* invoke da_xfd_callback once manually to process all events which
* were generated by the above work... after this, it will be called
* by the framework whenever there is activity on the server socket
*/
da_xfd_callback (0, 0);
/* now that we're up, add the X file descriptor to the main poll
* loop and bind it to our event handler
*/
return dockapp_add_pollfd (
XConnectionNumber (display),
POLLIN | POLLPRI,
da_xfd_callback,
0);
}
void dockapp_copy_pixmap (
int source_x, int source_y,
int target_x, int target_y,
int w, int h)
{
XCopyArea (
display,
icon_pixmap,
icon_pixmap,
copy_gc,
source_x, source_y,
w, h,
target_x, target_y);
da_redraw_icon ();
}
void dockapp_overlay_pixmap (
int source_x, int source_y,
int target_x, int target_y,
int w, int h)
{
XCopyArea (
display,
icon_pixmap,
icon_pixmap,
xor_gc,
source_x, source_y,
w, h,
target_x, target_y);
da_redraw_icon ();
}

158
wmget/dockapp/dockapp.h Normal file
View file

@ -0,0 +1,158 @@
#ifndef I_DOCKAPP_H
#define I_DOCKAPP_H
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
dockapp/dockapp.h - Public interface to the dockapp library
The dockapp library, distributed as part of the wmget program,
provides a fairly simple way to write dockapps. It abstracts away
the actual drawing, input, and main-loop logic.
*/
#include <X11/X.h> /* to get Button?Mask, etc. */
#include <sys/poll.h> /* to get POLLIN, POLLOUT, etc. */
/**********************************************************************
* BASIC TYPES
*/
/* RV type. When writing a callback, you should generally return ok or
* exit, to ask the dockapp to proceed or quit.
*/
typedef enum {
dockapp_ok = 0,
dockapp_exit,
dockapp_invalid_arg,
dockapp_rv_too_many_clickregions,
} dockapp_rv_t;
/**********************************************************************
* STARTUP/RUNTIME
*/
/* initializes the dockapp's X display
*/
dockapp_rv_t dockapp_init_gui (
char *appname, /* desired dockapp name */
char *argv[], /* X/dockapp args */
char **xpmdata); /* XPM data */
/* the main loop... when this returns, your program should exit
*/
dockapp_rv_t dockapp_run (void);
/* this asks dockapp_run() to periodically call an arbitrary function
* (you can only have one of these installed at a time). All sorts of
* other things can pre-empt a periodic callback, so consider the 'msec'
* parameter to be an interval floor.
*/
void dockapp_set_periodic_callback (
long msec, /* approx. interval */
dockapp_rv_t (*cb) (void *),
void *cbdata);
void dockapp_remove_periodic_callback (void);
/* you can ask dockapp_run() to add an fd to an internal efficient
* poll-list... see poll(2) for full documentation; the semantics here
* are that you give fd and pollevents and we create a struct pollfd;
* when we invoke the callback, the pollstatus arg will contain the
* revents field from the struct pollfd
*/
dockapp_rv_t dockapp_add_pollfd (
int fd, /* the fd to poll */
short pollevents, /* see poll(2), e.g. POLLIN */
dockapp_rv_t (*cb) (void *, short pollstatus),
void *cbdata);
dockapp_rv_t dockapp_remove_pollfd (
int fd); /* the fd to remove */
/**********************************************************************
* CLICKREGIONS
*/
/* a clickregion is simply an area where the user can click to trigger a
* callback
* (Helpful list of buttonmask components: ShiftMask, LockMask,
* ControlMask, Mod1Mask .. Mod5Mask, Button1Mask .. Button5Mask)
*/
dockapp_rv_t dockapp_add_clickregion (
int x, int y, int w, int h,
int buttonmask,
dockapp_rv_t (*cb) (void *, int x, int y),
void *cbdata);
/**********************************************************************
* PIXMAP COPY/OVERLAY OPERATIONS
* Most dockapp drawing will consist of simple pixel moving from one
* part of the pixmap to another.
*/
/* copy pixels, overwriting the target
*/
void dockapp_copy_pixmap (
int source_x, int source_y, /* copy from where */
int target_x, int target_y, /* copy to where */
int w, int h); /* copy how much */
/* use an XOR raster-op to overlay the source on the target; call this
* again with the same arguments to undo the first overlay
*/
void dockapp_overlay_pixmap (
int source_x, int source_y, /* copy from where */
int target_x, int target_y, /* copy to where */
int w, int h); /* copy how much */
/**********************************************************************
* X SELECTION
*/
/* request the current X selection... only non-incremental strings
* supported... if there is no selection, callback gets called with
* (const char *)0; otherwise, it gets called with the string (which
* will still be owned by the X server, so you need to copy it if you
* want to keep it)
*/
dockapp_rv_t dockapp_request_selection_string (
dockapp_rv_t (*cb) (void *, const char *),
void *cbdata);
#endif /* I_DOCKAPP_H */

233
wmget/iq.c Normal file
View file

@ -0,0 +1,233 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
iq.c - Core functions implementing the server's Unix-domain socket
To submit jobs to the server's input queue, you connect to a Unix-
domain socket it opens. These functions are responsible for
setting up, connecting to, and accepting connections from that
socket, and are used by server.c and request.c.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include "wmget.h"
/* iqsun == the Unix-domain socket address, aka the pathname, of our
* socket. iqsun_len == the number of bytes in iqsun incl. the header
* and the pathname string, excluding the unused bytes after the
* string's terminator.
*/
static struct sockaddr_un iqsun;
static int iqsun_len = 0; /* 0 => not yet initialized */
const char *iqname = ".wmget.iq";
int init_paths (void)
{
return 0;
}
static int iq_init_address (void)
{
const char *homedir = home_directory ();
if (iqsun_len) /* already initialized */
return 0;
/* Our pathname length is constrained by sizeof(sockaddr_un), and
* must be able to fit the homedir, the /, the iqname, and the \0.
*/
if (strlen (homedir) > sizeof iqsun.sun_path - 2 - sizeof iqname) {
error ("Home directory path is too long, can't construct "
"socket name");
return 1;
}
sprintf (iqsun.sun_path, "%s/%s", homedir, iqname);
debug ("IQ = '%s'", iqsun.sun_path);
iqsun.sun_family = AF_UNIX;
iqsun_len = sizeof iqsun - sizeof iqsun.sun_path
+ strlen (iqsun.sun_path) + 1;
return 0;
}
FILE *iq_client_connect (void)
{
int fd;
FILE *fp;
if (iq_init_address ())
return 0;
if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
error_sys ("Could not create Unix-domain socket to talk to server");
return 0;
}
if (connect (fd, (struct sockaddr *)&iqsun, iqsun_len) < 0) {
error_sys ("Could not connect to the server");
close (fd);
return 0;
}
if (!(fp = fdopen (fd, "r+"))) {
/* this should never fail for any reparable reason */
error_sys ("fdopen");
close (fd);
return 0;
}
return fp;
}
/* Server listener socket */
static int iq_listen_fd;
/** was going to use this in atexit(), but the problem is that we fork,
* and those children inherit the atexits, and well ...
static void iq_server_cleanup (void)
{
close (iq_listen_fd);
unlink (iqsun.sun_path);
}
*/
int iq_server_init (void)
{
int fd, test_fd;
if (iq_init_address ())
return 1;
if ((fd = socket (AF_UNIX, SOCK_STREAM, 0)) < 0) {
error_sys ("Could not create Unix-domain socket to receive requests");
return 1;
}
/* We're going to listen on this socket and don't want accept() to
* block
*/
if (fcntl (fd, F_SETFL, (long)O_NONBLOCK) < 0) {
error_sys ("fcntl");
close (fd);
return 1;
}
/* Before proceeding, try to *connect* to the iq. If this succeeds,
* then uh-oh, there's another wmget running out there.
*/
test_fd = connect (fd, (struct sockaddr *)&iqsun, iqsun_len);
if (test_fd >= 0) {
close (test_fd);
error (
"There's another wmget dock running. You can only run "
"one at a time.");
return 1;
}
/* Good. Just in case the pathname exists, unlink it. */
unlink (iqsun.sun_path);
if (bind (fd, (struct sockaddr *)&iqsun, iqsun_len) < 0) {
error_sys ("bind");
close (fd);
return 1;
}
/* Tighten up the new filename's permissions. */
if (chmod (iqsun.sun_path, S_IRWXU) < 0) {
error_sys ("chmod");
close (fd);
return 1;
}
if (listen (fd, 5) < 0) {
error_sys ("listen");
close (fd);
return 1;
}
iq_listen_fd = fd;
return 0;
}
FILE *iq_server_accept (void)
{
int fd;
FILE *fp;
struct sockaddr_un sun;
int sun_len = sizeof sun;
if ((fd = accept (iq_listen_fd, (struct sockaddr *)&sun,
&sun_len)) < 0) {
if (errno == EAGAIN) {
/* Simply no connections waiting. */
return 0;
}
error_sys ("accept");
return 0;
}
debug ("got a connection...");
if (!(fp = fdopen (fd, "r+"))) {
error_sys ("fdopen");
close (fd);
return 0;
}
return fp;
}
int iq_get_listen_fd (void)
{
return iq_listen_fd;
}

89
wmget/list.c Normal file
View file

@ -0,0 +1,89 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
list.c - Client code for retrieving the current job list
When invoked with the ``list'' argument, main() calls list(),
defined below. This code sends a LIST command to the server and
dumps the returned list to stdout.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "wmget.h"
int list (int argc, char **argv)
{
char line[MAXCMDLEN + 1];
char *word_break;
FILE *fp;
/* No additional options or arguments.
*/
if (argc > 2) {
error ("Extra arguments: list takes none");
return 1;
}
(void)argv;
if (!(fp = iq_client_connect ()))
return 1;
if (fprintf (fp, "LIST\r\n") == EOF) {
error_sys ("Could not submit command to server");
fclose (fp);
return 1;
}
if (!fgets (line, sizeof line - 1, fp)) {
error ("Server did not respond to command!");
fclose (fp);
return 1;
}
/* Extract the first word and compare. */
word_break = line + strcspn (line, " \t\r\n");
if (*word_break)
*word_break++ = 0;
if (strcasecmp (line, RESPONSE_LIST_COMING)) {
error ("Server responded with error: %s", word_break);
fclose (fp);
return 1;
}
while (fgets (line, sizeof line - 1, fp)) {
fputs (line, stdout);
}
fclose (fp);
return 0;
}

106
wmget/messages.c Normal file
View file

@ -0,0 +1,106 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
messages.c - functions for writing error, status, & debug messages
*/
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include "wmget.h"
static OutputLevel output_level_;
void set_output_level (OutputLevel lev)
{
output_level_ = lev;
}
OutputLevel output_level (void)
{
return output_level_;
}
void error (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputs ("\n", stderr);
}
void error_sys (const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
perror (" ");
}
void info (const char *fmt, ...)
{
va_list ap;
if (output_level_ < OL_NORMAL)
return;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputs ("\n", stderr);
}
void debug (const char *fmt, ...)
{
va_list ap;
if (output_level_ < OL_DEBUG)
return;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputs ("\n", stderr);
}
void debug_sys (const char *fmt, ...)
{
va_list ap;
if (output_level_ < OL_DEBUG)
return;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
perror (" ");
}

216
wmget/request.c Normal file
View file

@ -0,0 +1,216 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
request.c - Client code for submitting download requests
Invoked whenever wmget is run in "request" mode; submits a job to
the server dockapp.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include "wmget.h"
static char *enquote_strdup (const char *string)
{
int len = strlen (string);
char *newstr;
const char *src;
char *dest;
for (src = string; *src; src += strcspn (src, ")\\")) {
++len;
}
dest = newstr = malloc (len + 1);
for (src = string; *src; ++src) {
switch (*src) {
case ')':
case '\\':
*dest++ = '\\';
}
*dest++ = *src;
}
*dest = '\0';
return newstr;
}
int add_arg_s (char *line, const char *argname, const char *argval)
{
char *qargval;
if (strlen (line) + strlen (argname) + strlen (argval) + 3
> MAXCMDLEN) {
error ("Too much data to send to server!");
return 1;
}
qargval = enquote_strdup (argval);
line += strlen (line);
sprintf (line, " %s(%s)", argname, qargval);
free (qargval);
return 0;
}
int add_arg_i (char *line, const char *argname, int argval)
{
if (strlen (line) + strlen (argname) + 10
> MAXCMDLEN) {
error ("Too much data to send to server!");
return 1;
}
line += strlen (line);
sprintf (line, " %s(%d)", argname, argval);
return 0;
}
int request (int argc, char **argv)
{
char line[MAXCMDLEN + 1];
char *word_break;
FILE *fp;
Request req;
config_request (argc, argv, &req);
if (!req.source_url) {
error ("Missing source URL!");
usage ();
return 1;
}
strcpy (line, CMD_GET);
if (add_arg_s (line, ARG_GET_SOURCE_URL, req.source_url))
return 1;
if (req.display)
if (add_arg_s (line, ARG_GET_DISPLAY, req.display))
return 1;
if (req.save_to)
if (add_arg_s (line, ARG_GET_SAVE_TO, req.save_to))
return 1;
if (req.overwrite != -1)
if (add_arg_i (line, ARG_GET_OVERWRITE, req.overwrite))
return 1;
if (req.continue_from != -1)
if (add_arg_i (line, ARG_GET_CONTINUE_FROM, req.continue_from))
return 1;
if (req.proxy)
if (add_arg_s (line, ARG_GET_PROXY, req.proxy))
return 1;
if (req.follow != -1)
if (add_arg_i (line, ARG_GET_FOLLOW, req.follow))
return 1;
if (req.user_agent)
if (add_arg_s (line, ARG_GET_UA, req.user_agent))
return 1;
if (req.use_ascii != -1)
if (add_arg_i (line, ARG_GET_USE_ASCII, req.use_ascii))
return 1;
if (req.referer)
if (add_arg_s (line, ARG_GET_REFERER, req.referer))
return 1;
if (req.include != -1)
if (add_arg_i (line, ARG_GET_INCLUDE, req.include))
return 1;
if (req.interface)
if (add_arg_s (line, ARG_GET_INTERFACE, req.interface))
return 1;
if (req.proxy_auth)
if (add_arg_s (line, ARG_GET_PROXY_AUTH, req.proxy_auth))
return 1;
if (req.auth)
if (add_arg_s (line, ARG_GET_AUTH, req.auth))
return 1;
debug ("Command line is '%s'", line);
strcat (line, "\n");
if (!(fp = iq_client_connect ()))
return 1;
if (fputs (line, fp) == EOF) {
error_sys ("Failed to send command to server (fputs)");
fclose (fp);
return 1;
}
if (!fgets (line, sizeof line - 1, fp)) {
error ("Server did not respond to command!");
fclose (fp);
return 1;
}
/* Extract the first word and compare. */
word_break = line + strcspn (line, " \t\r\n");
while (*word_break && isspace (*word_break))
*word_break++ = 0;
if (strcasecmp (line, RESPONSE_JOB_ACCEPTED)) {
error ("%s", word_break);
fclose (fp);
return 1;
}
fclose (fp);
/* Since we got a RESPONSE_JOB_ACCEPTED, present the user with the
* job ID for future cancellation.
*/
printf ("Job ID is %s\n", word_break);
return 0;
}

225
wmget/retrieve.c Normal file
View file

@ -0,0 +1,225 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
retrieve.c - Child process code for performing downloads
When the server [server.c] accepts a download job, it fork()s a
new process which in turn invokes this file's retrieve(). This
code takes care of setting up and invoking libcurl to grab the
data. The shared memory segment (shmem, wmget.h) is inherited from
the server and contains the Job structure through which the child
process communicates with the server.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include "wmget.h"
#if LIBCURL_VERSION_NUM >= 0x070905
# define PROGRESS double
#else
# define PROGRESS size_t
#endif
static int progress_callback (
void *data,
PROGRESS total,
PROGRESS progress,
PROGRESS unused1,
PROGRESS unused2)
{
Job *job = data;
(void)unused1;
(void)unused2;
if (job->stop_request) {
/* Abort transfer. */
job->status = J_STOPPING;
return 1;
}
if (!total) {
debug ("Total bytes unknown!");
total = progress * 2 + 1; /* just to make the bar halfway */
}
job->progress = (unsigned long)progress;
job->prog_max = (unsigned long)total;
debug ("progress_callback (%d/%d)", job->progress, job->prog_max);
return 0;
}
void write_error_file (Job *job, const char *msg)
{
char error_file_name[MAXPATHLEN + 1];
FILE *error_file;
strcpy (error_file_name, job->options.save_to);
strncat (error_file_name, ".ERROR",
MAXPATHLEN - strlen (error_file_name));
if (!(error_file = fopen (error_file_name, "w")))
return;
fprintf (error_file, "Download failed:\n");
fprintf (error_file, " From URL: %s\n", job->source_url);
fprintf (error_file, " To file: %s\n", job->options.save_to);
fprintf (error_file, " Error: %s\n", msg);
fprintf (error_file, " (" WMGET_VERSION_BANNER ")\n");
}
int retrieve (Job *job)
{
CURL *curl;
CURLcode rc;
FILE *outfp;
JobOptions *opts;
debug ("Retrieval process %d running job:", getpid());
debug_dump_job (job);
if (job->options.continue_from) {
outfp = fopen (job->options.save_to, "a");
} else {
outfp = fopen (job->options.save_to, "w");
}
if (!outfp) {
error_sys ("could not open `%s' for output",
job->options.save_to);
return 1;
}
curl = curl_easy_init ();
if (!curl) {
error ("could not initialize libcurl");
write_error_file (job, "could not initialize libcurl");
fclose (outfp);
return 1;
}
curl_easy_setopt (curl, CURLOPT_FILE, outfp);
curl_easy_setopt (curl, CURLOPT_URL, job->source_url);
curl_easy_setopt (
curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, (void *)job);
curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, job->error);
curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0);
if (job->options.continue_from) {
curl_easy_setopt (curl, CURLOPT_RESUME_FROM,
(long)job->options.continue_from);
}
/* Now load the job's user-configurable parameters:
*/
opts = &job->options;
if (opts->proxy[0]) {
curl_easy_setopt (curl, CURLOPT_PROXY, opts->proxy);
}
if (opts->follow) {
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt (curl, CURLOPT_MAXREDIRS, opts->follow);
} else {
curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 0);
}
if (opts->user_agent[0]) {
curl_easy_setopt (curl, CURLOPT_USERAGENT, opts->user_agent);
}
if (opts->use_ascii) {
curl_easy_setopt (curl, CURLOPT_TRANSFERTEXT, 1);
}
if (opts->referer[0]) {
curl_easy_setopt (curl, CURLOPT_REFERER, opts->referer);
}
if (opts->include) {
curl_easy_setopt (curl, CURLOPT_HEADER, 1);
}
if (opts->interface[0]) {
curl_easy_setopt (curl, CURLOPT_INTERFACE, opts->interface);
}
if (opts->proxy_auth[0]) {
curl_easy_setopt (curl, CURLOPT_PROXYUSERPWD, opts->proxy_auth);
}
if (opts->auth[0]) {
curl_easy_setopt (curl, CURLOPT_USERPWD, opts->auth);
}
/* If wmget is verbose, set libcurl to verbose too...
*/
if (output_level () > OL_NORMAL)
curl_easy_setopt (curl, CURLOPT_VERBOSE, 1);
/* Finally, perform the download:
*/
job->status = J_RUNNING;
rc = curl_easy_perform (curl);
if (rc) {
if (job->status == J_STOPPING) {
info ("aborted by user");
job->status = J_COMPLETE;
} else {
error (job->error);
write_error_file (job, job->error);
job->status = J_COMPLETE;
}
} else {
job->status = J_COMPLETE;
}
curl_easy_cleanup (curl);
fclose (outfp);
if (rc)
return 1;
return 0;
}

1096
wmget/server.c Normal file

File diff suppressed because it is too large Load diff

62
wmget/usage.c Normal file
View file

@ -0,0 +1,62 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
usage.c - Displays usage information
Provides usage(), which gets invoked whenever the user asks for
help, or provides bad options.
*/
#include <stdio.h>
#include "wmget.h"
void usage (void)
{
printf (
WMGET_VERSION_BANNER "\n"
WMGET_COPYRIGHT "\n"
"Usage:\n"
" wmget dock [options] # To start up the dockapp\n"
" wmget [options] <URL> # To request a download\n"
" wmget cancel <job-id> # To cancel a download\n"
" wmget list # To show current and pending jobs\n"
" wmget --version # (or -v) To print the version\n"
" wmget --help # (or -h) To print this text\n"
"Options:\n");
#define yes " ..."
#define no " "
#define O(s,l,a,t) \
printf (" -%c|--%-15s " t "\n", s, #l a);
#include "config.def"
#undef O
#undef no
#undef optional
#undef required
}

45
wmget/wmget-test.pl Executable file
View file

@ -0,0 +1,45 @@
#!/usr/bin/perl -w
use strict;
use IO::Handle;
use IO::Socket;
&run_server ();
sub run_server {
local $SIG{PIPE} = 'IGNORE';
my $servsock = new IO::Socket (
Domain => AF_INET,
Type => SOCK_STREAM,
Proto => "tcp",
Reuse => 1,
Listen => 1,
LocalPort => 8000,
) or die "new IO::Socket: $!";
print "to test, enter wmget http://localhost:8000/...\n";
while (my $client = $servsock->accept) {
$client->autoflush (1);
while (<$client>) {
print STDERR "> $_";
last if not /\S/;
}
print STDERR "headers done. sending data...\n";
print $client "HTTP/1.0 200 Ok, here you go...\r\n";
print $client "Content-Type: text/plain\r\n";
print $client "Content-Length: 1000\r\n\r\n";
# generate bogus data, and do it slowly....
for (1..10) {
print $client "x" x 99, "\n";
sleep 1;
}
}
}

217
wmget/wmget.1 Normal file
View file

@ -0,0 +1,217 @@
.\"Generated by db2man.xsl. Don't modify this, modify the source.
.de Sh \" Subsection
.br
.if t .Sp
.ne 5
.PP
\fB\\$1\fR
.PP
..
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Ip \" List item
.br
.ie \\n(.$>=3 .ne \\$3
.el .ne 3
.IP "\\$1" \\$2
..
.TH "WMGET" 1 "" "" ""
.SH NAME
wmget \- Background download manager in a dockapp
.SH "SYNOPSIS"
.ad l
.hy 0
.HP 6
\fBwmget\fR dock [\fIoptions\fR]
.ad
.hy
.ad l
.hy 0
.HP 6
\fBwmget\fR [\fIoptions\fR] {\fIURL\fR}
.ad
.hy
.ad l
.hy 0
.HP 6
\fBwmget\fR cancel {\fIjob\-id\fR}
.ad
.hy
.ad l
.hy 0
.HP 6
\fBwmget\fR list
.ad
.hy
.SH "DESCRIPTION"
.PP
wmget is a ``dockapp'' which makes it more convenient to retrieve files in the background\&. Dockapps are applications which run in small windows intended to be ``docked'' in window manager\-provided locations\&. wmget was developed primarily under GNU Window Maker, the author's preferred WM, but is known to work under AfterStep as well, and should work with other dockapp\-aware window managers and docks\&.
.PP
It uses the excellent libcurl library, part of the Curl automated\-download program, to perform file retrieval from Web servers, FTP servers, and other sources\&.
.PP
wmget allows you to perform multiple downloads without keeping a terminal open (for FTP or curl or something) or another window on your desktop (e\&.g\&. for Mozilla download progress); download progress is visible any time the Dock is visible\&.
.PP
You start downloads either by ``pasting'' URLs from Web browsers or other applications, or by invoking wmget from the command line (or another script or program) with a source URL\&. The dockapp has a handful of configurable download options, such as target directory, HTTP proxy server, etc\&.
.SH "STARTING UP"
.PP
To start the dockapp, just run \fBwmget dock &\fR\&. If you are running Window Maker, you can then just drag the new appicon onto your Dock, right\-click on an area outside the four progress bars, select Settings, and select Start when Window Maker is started\&.
.PP
If you are running AfterStep, you can add it to your Wharf by adding the following line to your \fI~/GNUstep/Library/AfterStep/wharf\fR file:
.nf
*Wharf wmget \- Swallow "wmget" wmget dock &
.fi
.PP
Other window managers support dockapps in different ways\&. Even in window managers without any special dockapp support, you can run wmget as noted above; it will simply show up as a small window or "icon"\&.
.SH "USING WMGET"
.PP
wmget's user interface is simple: four stacked progress bars, initially empty, representing four possible simultaneous downloads\&. The top bar will say ``wmget'' when there isn't a download running there, but any download will cover that up\&.
.PP
Each running download normally shows up to nine characters of its filename, overlaid with a progress bar\&. You can click on any progress bar to reveal a percentage display and a stop button; clicking on the percentage display switches back, while clicking on the stop button stops the download\&. There is currently no confirmation; it just stops\&.
.PP
You can ``request'' downloads at any time\&. If all four places show running downloads, additional requests will queue up, waiting for one to complete; wmget will never be downloading more than four files at a time\&.
.PP
By default, wmget figures out a reasonable filename for any requested downloads, writes them to your home directory, and won't overwrite an existing file by the same name\&. All of these, along with a few other options, are configurable\&. See below\&.
.SS "Requesting Downloads with the Mouse"
.PP
The easiest way to request a download is by copying and pasting a link\&. wmget lets you paste a URL by middle\-clicking anywhere on any of its status bars\&. Simply copy a link from some other source (for example, by right\-clicking on a link in Mozilla or Netscape and picking Copy Link Location), and middle\-click on one of the progress meter boxes in wmget\&.
.SS "Requesting Downloads from the Command Line"
.PP
The \fBwmget\fR command also lets you directly request downloads from the command line, or from within a script or another program\&. The syntax is \fBwmget \fIURL\fR\fR, plus any of the options documented below\&.
.PP
Once you run this command, you'll either get an error message or a ``job ID''\&. The job ID is only useful in conjunction with the \fBwmget cancel\fR command\&.
.SS "Download Failures"
.PP
Downloads can fail for a variety of reasons, from running out of disk space to modem hangups\&. Since wmget is designed not to interrupt your workflow or exceed its little square window, it responds to any download error by aborting the download and writing an error file to your download directory\&. This error file has the name \fIfile\&.ERROR\fR, where \fIfile\fR is the name of the actual download target\&. This error file is a plain text file containing information on what you were downloading and what went wrong\&.
.SS "Viewing and Canceling Downloads"
.PP
As noted above, you can see the currently\-running downloads in the four progress boxes on the dockapp\&. Clicking on a bar reveals a stop button, and clicking on that stop button cancels the download (but leaves the partially\-downloaded file on your computer)\&.
.PP
At any time, you can also run the \fBwmget list\fR command, which displays all the running downloads as well as any queued\-up requests\&. The listing contains entries like this:
.IP
.nf
Job 10 [linux\-2\&.6]: 1658544/33073407 RUNNING
ftp://ftp\&.kernel\&.org/pub/linux/kernel/v2\&.6/linux\-2\&.6\&.0\-test6\&.tar\&.bz2
=> /home/aaron/DOWNLOAD/linux\-2\&.6\&.0\-test6\&.tar\&.bz2
.fi
.PP
What you see in that (admittedly dense) listing are the job ID, the name of the download as displayed on the dockapp (surrounded in brackets), the progress in bytes, the total bytes to download, the current status, the source URL, and the target file on your computer\&. Whew\&.
.PP
You can cancel any requested or running download from the command line by specifying \fBwmget cancel \fIjob\-id\fR\fR\&.
.SH "COMMAND-LINE OPTIONS AND THE CONFIGURATION FILE"
.PP
wmget supports a handful of configuration options\&. You can specify defaults for all downloads by putting them in a configuration file or adding command\-line options to the \fBwmget dock\fR command at startup, or you can specify options for one specific download by adding options to the \fBwmget \fIURL\fR\fR command when you request them\&. There isn't any way to specify options on URLs you paste with the mouse\&. Dockapp command\-line arguments override config\-file settings, and per\-URL settings override dockapp settings\&.
.PP
The configuration file is an optional file named \fI\&.wmgetrc\fR in your home directory\&. If it's there, it's parsed by the dockapp at startup\&. The syntax is simple: one option per line, all options consisting of a name and possibly a value\&. Blank lines are okay, and lines starting with # are ignored (so you can disable options easily)\&. Option names are just the same as the command\-line option names given below, except you don't put the dashes (``\-\-'') and you can't use the one\-letter abbreviations\&.
.TP
\-\-version, \-v
Regardless of any other options, this prints out version and copyright information and exits\&.
.TP
\-\-help, \-h
Regardless of any other options, this prints out a help message and exits\&.
.TP
\-\-silent, \-s
Suppress any output text other than error messages\&.
.TP
\-\-verbose, \-V
Write extra debugging information; not very useful unless you're debugging or extending the software\&.
.TP
\-\-output \fIpathname\fR, \-o \fIpathname\fR
Specifies where to write downloaded files\&. In the config file or on the dockapp command line, this can only be used to specify your default download directory; it must be an existing directory, and if it's not absolute then it is assumed to be relative to your home directory\&. On a specific download request, this can provide an alternate save directory or even an alternate filename; in that case, a non\-absolute path is relative to the default download directory\&.
.TP
\-\-display \fIname\fR, \-d \fIname\fR
Display the first nine characters of \fIname\fR in the progress display for this file\&. (Only valid on specific download requests, not on the dockapp or in the config file\&.)
.TP
\-\-overwrite, \-O
Allow wmget to overwrite an existing file when downloading\&. Normally, it will refuse to do so\&.
.TP
\-\-continue, \-C
When fetching a file that already exists locally, assume the local copy was an aborted download and try to download just the remainder\&.
.TP
\-\-auth \fIusername\fR:\fIpassword\fR, \-a \fIusername\fR:\fIpassword\fR
Provides login information for the server from which you're downloading\&.
.TP
\-\-proxy \fIserver\fR:\fIport\fR, \-p \fIserver\fR:\fIport\fR, \-\-proxy_auth \fIuser\fR:\fIpassword\fR, \-P \fIuser\fR:\fIpassword\fR
Specifies a proxy server and optionally a proxy\-server username/password pair for getting past firewalls\&.
.TP
\-\-follow \fIN\fR, \-f \fIN\fR
Specifies how many HTTP redirects to follow when resolving a page; by default, wmget is configured to follow up to 5\&. Set this to 0 to disable redirection\&. (In any real\-world situation, if you're getting redirected more than 5 times, there's a problem\&.\&.\&.)
.TP
\-\-user\-agent \fIstring\fR, \-U \fIstring\fR
Specifies which User\-Agent string to provide to servers when performing HTTP downloads\&. The default User\-Agent names both the wmget and libcurl versions in use\&.
.TP
\-\-ascii, \-B
Force FTP downloads to use ASCII mode; normally, they use binary mode\&. If you're downloading text documents, ASCII mode will take care of any necessary conversions between the text formats of the server and your computer\&.
.TP
\-\-referer \fIstring\fR, \-e \fIstring\fR
Provides a ``referer'' string to the Web server\&.
.TP
\-\-interface \fIinterface\fR, \-\-n \fIinterface\fR
Names a specific network interface to use (e\&.g\&., eth0 for the first Ethernet interface on a Linux system)\&. Rarely needed\&.
.TP
\-\-headers, \-h
When performing an HTTP retrieval, include the HTTP message header in the saved file\&. This is only really useful for testing\&.
.SH "FILES"
.TP
\fI~/\&.wmgetrc\fR
The (optional) configuration file for the wmget dockapp\&. Settings in this file are used to specify defaults for the dockapp when it starts; see the section on configuration and command\-line options for more details\&.
.TP
\fI~/\&.wmget\&.iq\fR
A Unix\-domain socket created by the wmget dockapp to accept requests from wmget commands\&. Created at startup automatically\&.
.SH AUTHOR
Aaron Trickey.

127
wmget/wmget.c Normal file
View file

@ -0,0 +1,127 @@
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
wmget.c - main() and a few common functions
This file contains the main() for wmget; it simply checks for the
presence of ``dock'' as the first argument and invokes server(),
request(), cancel(), or list() as appropriate. Also found here are
a few global utilities.
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "wmget.h"
void debug_dump_job (Job *job)
{
if (output_level () < OL_DEBUG)
return;
debug ("id = %lu\n"
"status = %d\n"
"progress=%d/%d\n"
"stop_request=%d\n"
"display=[%s]\n"
"source_url=[%s]\n"
"save_to=[%s]\n"
"continue_from=%d\n\n",
job->job_id,
job->status, job->progress, job->stop_request,
job->prog_max, job->options.display, job->source_url,
job->options.save_to, job->options.continue_from);
}
const char *home_directory (void)
{
static char *home_directory = 0;
struct passwd *pwent;
if (home_directory)
return home_directory;
home_directory = getenv ("HOME");
if (!home_directory) {
pwent = getpwuid (geteuid ());
home_directory = strdup (pwent->pw_dir);
}
return home_directory;
}
int main (int argc, char **argv)
{
char **arg;
int (*function) (int, char **) = &request;
if (argc < 2) {
usage ();
return 0;
}
/* Cheat, because we don't invoke getopt until we know what mode
* we're in...
*/
for (arg = argv; *arg; ++arg) {
if (strcmp (*arg, "dock") == 0) {
function = &server;
} else if (strcmp (*arg, "cancel") == 0) {
function = &cancel;
} else if (strcmp (*arg, "list") == 0) {
function = &list;
} else if (strcmp (*arg, "-v") == 0
|| strcmp (*arg, "--version") == 0) {
puts (WMGET_VERSION_BANNER);
puts (WMGET_COPYRIGHT);
return 0;
} else if (strcmp (*arg, "-h") == 0
|| strcmp (*arg, "--help") == 0) {
usage ();
return 0;
}
}
return function (argc, argv);
}

251
wmget/wmget.h Normal file
View file

@ -0,0 +1,251 @@
#ifndef I_WMGET_H
#define I_WMGET_H
/*
wmget - A background download manager as a Window Maker dock app
Copyright (c) 2001-2003 Aaron Trickey <aaron@amtrickey.net>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
********************************************************************
wmget.h - Common definitions for all wmget modules
This file defines the public entry points in each *.c file in the
main wmget program, as well as some common constants, global
variables, and the structure of the shared memory segment.
*/
#include <sys/param.h>
#include <curl/curl.h>
/* Important: the Makefile greps the source to extract WMGET_VERSION.
* So don't change its format or anything without checking there first.
*/
#define WMGET_VERSION "0.6.0"
#define WMGET_VERSION_BANNER "wmget " WMGET_VERSION \
", compiled with libcurl " \
LIBCURL_VERSION
#define WMGET_COPYRIGHT "Copyright (c) 2001-2003 " \
"Aaron Trickey <aaron@amtrickey.net>" \
"; ABSOLUTELY NO WARRANTY"
#define DEFAULT_USER_AGENT \
"wmget/" WMGET_VERSION " (libcurl/" LIBCURL_VERSION ")"
#define MAXRCLINELEN 1024
#define MAXURL 1024
#define MAXUA 255
#define MAXAUTH 100
#define MAXIF 255
#define MAXCMDLEN 2048
#define MAXCMDARGS 20
#define MAX_DISPLAY 9
#define MAX_ACTIVE_JOBS 4 /* a number constrained by the UI */
#define MAX_QUEUED_JOBS 20
/* Command language */
/* The GET command and its arguments: */
#define CMD_GET "GET"
#define ARG_GET_SOURCE_URL "FROM"
#define ARG_GET_DISPLAY "DISP"
#define ARG_GET_SAVE_TO "TO"
#define ARG_GET_OVERWRITE "OVER"
#define ARG_GET_CONTINUE_FROM "CONT"
#define ARG_GET_PROXY "PROXY"
#define ARG_GET_FOLLOW "FOLLOW"
#define ARG_GET_UA "UA"
#define ARG_GET_USE_ASCII "ASCII"
#define ARG_GET_REFERER "REF"
#define ARG_GET_INCLUDE "INCL"
#define ARG_GET_INTERFACE "IF"
#define ARG_GET_PROXY_AUTH "PROXY-AUTH"
#define ARG_GET_AUTH "AUTH"
#define CMD_CANCEL "CANCEL"
#define ARG_CANCEL_JOBID "JOBID"
#define CMD_LIST "LIST"
#define RESPONSE_JOB_ACCEPTED "ACCEPTED"
#define RESPONSE_JOB_CANCELED "CANCELED"
#define RESPONSE_LIST_COMING "JOBLIST"
#define RESPONSE_ERROR "ERROR"
/* Debug trace text output levels */
typedef enum {
OL_SILENT,
OL_NORMAL,
OL_DEBUG,
} OutputLevel;
/* The various states an individual job may be in.
*/
typedef enum {
J_EMPTY, /* this job slot is empty */
J_INIT, /* slot has been taken, job not yet started */
J_RUNNING, /* this job is running */
J_PAUSED, /* this job is paused */
J_STOPPING, /* a stop request has come from the user */
J_COMPLETE, /* job complete, cleaning up */
J_FAILED, /* job failed! */
} JobStatus;
/* The type of a job ID; these are allocated by the dockapp and are
* never reused within its lifetime.
*/
typedef unsigned long job_id_t;
/* User-configurable job options.
*/
typedef struct {
char display[MAX_DISPLAY + 1]; /* Text to display */
char save_to[MAXPATHLEN + 1]; /* Full pathname to save to */
/* (For srvr, this MUST be a dir) */
int overwrite; /* Allow overwrite of save_to? */
int continue_from; /* Byte# to resume from */
char proxy[MAXURL + 1]; /* Proxy to use (or empty string) */
int follow; /* How many redirects to follow */
char user_agent[MAXUA + 1]; /* User-agent string to provide */
int use_ascii; /* Force FTP to ASCII */
char referer[MAXURL + 1]; /* Specify referer */
int include; /* Include HTTP headers in output */
char interface[MAXIF + 1]; /* Limit to given interface */
char proxy_auth[MAXAUTH + 1]; /* Proxy authentication */
char auth[MAXAUTH + 1]; /* Site authentication */
} JobOptions;
/* A command-line download request. Strings are NULL if defaulted;
* integers are -1.
*/
typedef struct {
const char *source_url; /* MANDATORY. Duh. */
const char *display;
const char *save_to;
int overwrite;
int continue_from;
const char *proxy;
int follow;
const char *user_agent;
int use_ascii;
const char *referer;
int include;
const char *interface;
const char *proxy_auth;
const char *auth;
} Request;
/* The totality of a running or queued job:
*/
typedef struct {
job_id_t job_id;
JobStatus status;
char error[CURL_ERROR_SIZE + 1];
unsigned long progress;
unsigned long prog_max;
int stop_request;
char source_url[MAXURL + 1]; /* URL to fetch */
JobOptions options;
} Job;
/* The shared-memory structure containing the active job list. (Pending
* jobs are queued in the dockapp's private data segment.)
*/
typedef struct {
Job jobs[MAX_ACTIVE_JOBS];
} Shmem;
/* Specifies the server configuration. This is used only by server.c,
* and gets populated by config_server() in config.c.
*/
typedef struct {
JobOptions job_defaults;
} ServerConfig;
/* Convenience macro
*/
#define STRCPY_TO_ARRAY(to,from) \
do { \
strncpy (to, from, sizeof to); \
to[sizeof to - 1] = '\0'; \
} while (0)
/* configure.c */
extern void config_server (int argc, char **argv, ServerConfig *cfg);
extern void clear_request (Request *req);
extern void config_request (int argc, char **argv, Request *req);
/* usage.c */
extern void usage (void);
/* iq.c */
extern int iq_server_init (void); /* called once, by server */
extern FILE *iq_server_accept (void); /* returns new cxn or NULL */
extern FILE *iq_client_connect (void); /* called by each client */
extern int iq_get_listen_fd (void); /* so you can select/poll */
/* server.c */
extern Shmem *shmem;
extern int server (int argc, char **argv);
/* request.c */
extern int request (int argc, char **argv);
/* cancel.c */
extern int cancel (int argc, char **argv);
/* list.c */
extern int list (int argc, char **argv);
/* retrieve.c */
extern int retrieve (Job *job);
/* wmget.c */
extern const char *home_directory (void);
extern void debug_dump_job (Job *job);
/* messages.c */
extern void set_output_level (OutputLevel lev);
extern OutputLevel output_level (void);
extern void error (const char *fmt, ...);
extern void error_sys (const char *fmt, ...);
extern void info (const char *fmt, ...);
extern void debug (const char *fmt, ...);
extern void debug_sys (const char *fmt, ...);
#endif /* I_WMGET_H */

492
wmget/wmget.refentry.xml Normal file
View file

@ -0,0 +1,492 @@
<?xml version="1.0" standalone="no"?>
<?xml-stylesheet
href="http://docbook.sourceforge.net/release/xsl/1.62/manpages/docbook.xsl"
type="text/xsl"?>
<!--
<!DOCTYPE refentry
PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!DOCTYPE refentry
PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"/usr/local/docbook/docbookx.dtd">
-->
<!DOCTYPE refentry
PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
"http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd">
<refentry>
<refentryinfo>
<author>
<firstname>Aaron</firstname><surname>Trickey</surname>
<email>aaron@amtrickey.net</email>
</author>
<copyright>
<year>2001</year>
<year>2002</year>
<year>2003</year>
<holder>Aaron Trickey</holder>
</copyright>
</refentryinfo>
<refmeta>
<refentrytitle>wmget</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>wmget</refname>
<refpurpose>Background download manager in a dockapp</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>wmget</command>
<arg choice="plain">dock</arg>
<arg choice="opt"><replaceable>options</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>wmget</command>
<arg choice="opt"><replaceable>options</replaceable></arg>
<arg choice="req"><replaceable>URL</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>wmget</command>
<arg choice="plain">cancel</arg>
<arg choice="req"><replaceable>job-id</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>wmget</command>
<arg choice="plain">list</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
wmget is a ``dockapp'' which makes it more convenient to
retrieve files in the background. Dockapps are applications
which run in small windows intended to be ``docked'' in window
manager-provided locations. wmget was developed primarily under
GNU Window Maker, the author's preferred WM, but is known to
work under AfterStep as well, and should work with other
dockapp-aware window managers and docks.
</para>
<para>
It uses the excellent libcurl library, part of the Curl
automated-download program, to perform file retrieval from Web
servers, FTP servers, and other sources.
</para>
<para>
wmget allows you to perform multiple downloads without keeping a
terminal open (for FTP or curl or something) or another window
on your desktop (e.g. for Mozilla download progress); download
progress is visible any time the Dock is visible.
</para>
<para>
You start downloads either by ``pasting'' URLs from Web browsers
or other applications, or by invoking wmget from the command
line (or another script or program) with a source URL. The
dockapp has a handful of configurable download options, such as
target directory, HTTP proxy server, etc.
</para>
</refsect1>
<refsect1>
<title>Starting Up</title>
<para>
To start the dockapp, just run <command>wmget dock
&amp;</command>. If you are running Window Maker, you can then
just drag the new appicon onto your Dock, right-click on an area
outside the four progress bars, select
<guimenuitem>Settings</guimenuitem>, and select <guilabel>Start
when Window Maker is started</guilabel>.
</para>
<para>
If you are running AfterStep, you can add it to your Wharf by
adding the following line to your
<filename>~/GNUstep/Library/AfterStep/wharf</filename> file:
</para>
<informalfigure>
<programlisting>
*Wharf wmget - Swallow "wmget" wmget dock &amp;
</programlisting>
</informalfigure>
<para>
Other window managers support dockapps in different ways. Even in
window managers without any special dockapp support, you can run
wmget as noted above; it will simply show up as a small window or
"icon".
</para>
</refsect1>
<refsect1>
<title>Using wmget</title>
<para>
wmget's user interface is simple: four stacked progress bars,
initially empty, representing four possible simultaneous
downloads. The top bar will say ``wmget'' when there isn't a
download running there, but any download will cover that up.
</para>
<para>
Each running download normally shows up to nine characters of its
filename, overlaid with a progress bar. You can click on any
progress bar to reveal a percentage display and a stop button;
clicking on the percentage display switches back, while clicking
on the stop button stops the download. There is currently no
confirmation; it just stops.
</para>
<para>
You can ``request'' downloads at any time. If all four places
show running downloads, additional requests will queue up, waiting
for one to complete; wmget will never be downloading more than
four files at a time.
</para>
<para>
By default, wmget figures out a reasonable filename for any
requested downloads, writes them to your home directory, and won't
overwrite an existing file by the same name. All of these, along
with a few other options, are configurable. See below.
</para>
<refsect2>
<title>Requesting Downloads with the Mouse</title>
<para>
The easiest way to request a download is by copying and pasting
a link. wmget lets you paste a URL by middle-clicking anywhere
on any of its status bars. Simply copy a link from some other
source (for example, by right-clicking on a link in Mozilla or
Netscape and picking <guimenuitem>Copy Link
Location</guimenuitem>), and middle-click on one of the progress
meter boxes in wmget.
</para>
</refsect2>
<refsect2>
<title>Requesting Downloads from the Command Line</title>
<para>
The <command>wmget</command> command also lets you directly
request downloads from the command line, or from within a script
or another program. The syntax is <command>wmget
<replaceable>URL</replaceable></command>, plus any of the
options documented below.
</para>
<para>
Once you run this command, you'll either get an error message or
a ``job ID''. The job ID is only useful in conjunction with the
<command>wmget cancel</command> command.
</para>
</refsect2>
<refsect2>
<title>Download Failures</title>
<para>
Downloads can fail for a variety of reasons, from running out of
disk space to modem hangups. Since wmget is designed not to
interrupt your workflow or exceed its little square window, it
responds to any download error by aborting the download and
writing an error file to your download directory. This error
file has the name <filename>file.ERROR</filename>, where
<filename>file</filename> is the name of the actual download
target. This error file is a plain text file containing
information on what you were downloading and what went wrong.
</para>
</refsect2>
<refsect2>
<title>Viewing and Canceling Downloads</title>
<para>
As noted above, you can see the currently-running downloads in
the four progress boxes on the dockapp. Clicking on a bar
reveals a stop button, and clicking on that stop button cancels
the download (but leaves the partially-downloaded file on your
computer).
</para>
<para>
At any time, you can also run the <command>wmget list</command>
command, which displays all the running downloads as well as any
queued-up requests. The listing contains entries like this:
</para>
<informalfigure><screen
>Job 10 [linux-2.6]: 1658544/33073407 RUNNING
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.0-test6.tar.bz2
=&gt; /home/aaron/DOWNLOAD/linux-2.6.0-test6.tar.bz2
</screen></informalfigure>
<para>
What you see in that (admittedly dense) listing are the job ID,
the name of the download as displayed on the dockapp (surrounded
in brackets), the progress in bytes, the total bytes to
download, the current status, the source URL, and the target
file on your computer. Whew.
</para>
<para>
You can cancel any requested or running download from the
command line by specifying <command>wmget cancel
<replaceable>job-id</replaceable></command>.
</para>
</refsect2>
</refsect1>
<refsect1>
<title>Command-Line Options and the Configuration File</title>
<para>
wmget supports a handful of configuration options. You can
specify defaults for all downloads by putting them in a
configuration file or adding command-line options to the
<command>wmget dock</command> command at startup, or you can
specify options for one specific download by adding options to the
<command>wmget <replaceable>URL</replaceable></command> command
when you request them. There isn't any way to specify options on
URLs you paste with the mouse. Dockapp command-line arguments
override config-file settings, and per-URL settings override
dockapp settings.
</para>
<para>
The configuration file is an optional file named
<filename>.wmgetrc</filename> in your home directory. If it's
there, it's parsed by the dockapp at startup. The syntax is
simple: one option per line, all options consisting of a name and
possibly a value. Blank lines are okay, and lines starting with #
are ignored (so you can disable options easily). Option names are
just the same as the command-line option names given below, except
you don't put the dashes (``--'') and you can't use the one-letter
abbreviations.
</para>
<variablelist>
<varlistentry>
<term>--version</term>
<term>-v</term>
<listitem>
<para>
Regardless of any other options, this prints out version and
copyright information and exits.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--help</term>
<term>-h</term>
<listitem>
<para>
Regardless of any other options, this prints out a help
message and exits.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--silent</term>
<term>-s</term>
<listitem>
<para>
Suppress any output text other than error messages.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--verbose</term>
<term>-V</term>
<listitem>
<para>
Write extra debugging information; not very useful unless
you're debugging or extending the software.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--output <replaceable>pathname</replaceable></term>
<term>-o <replaceable>pathname</replaceable></term>
<listitem>
<para>
Specifies where to write downloaded files. In the config
file or on the dockapp command line, this can only be used
to specify your default download directory; it must be an
existing directory, and if it's not absolute then it is
assumed to be relative to your home directory. On a
specific download request, this can provide an alternate
save directory or even an alternate filename; in that case,
a non-absolute path is relative to the default download
directory.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--display <replaceable>name</replaceable></term>
<term>-d <replaceable>name</replaceable></term>
<listitem>
<para>
Display the first nine characters of
<replaceable>name</replaceable> in the progress display for
this file.
(Only valid on specific download requests, not on the
dockapp or in the config file.)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--overwrite</term>
<term>-O</term>
<listitem>
<para>
Allow wmget to overwrite an existing file when downloading.
Normally, it will refuse to do so.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--continue</term>
<term>-C</term>
<listitem>
<para>
When fetching a file that already exists locally, assume the
local copy was an aborted download and try to download just
the remainder.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--auth <replaceable>username</replaceable>:<replaceable>password</replaceable></term>
<term>-a <replaceable>username</replaceable>:<replaceable>password</replaceable></term>
<listitem>
<para>
Provides login information for the server from which you're
downloading.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--proxy
<replaceable>server</replaceable>:<replaceable>port</replaceable>
</term>
<term>-p
<replaceable>server</replaceable>:<replaceable>port</replaceable>
</term>
<term>--proxy_auth
<replaceable>user</replaceable>:<replaceable>password</replaceable>
</term>
<term>-P
<replaceable>user</replaceable>:<replaceable>password</replaceable>
</term>
<listitem>
<para>
Specifies a proxy server and optionally a proxy-server
username/password pair for getting past firewalls.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--follow <replaceable>N</replaceable></term>
<term>-f <replaceable>N</replaceable></term>
<listitem>
<para>
Specifies how many HTTP redirects to follow when resolving a
page; by default, wmget is configured to follow up to 5.
Set this to 0 to disable redirection. (In any real-world
situation, if you're getting redirected more than 5 times,
there's a problem...)
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--user-agent <replaceable>string</replaceable></term>
<term>-U <replaceable>string</replaceable></term>
<listitem>
<para>
Specifies which User-Agent string to provide to servers when
performing HTTP downloads. The default User-Agent names
both the wmget and libcurl versions in use.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--ascii</term>
<term>-B</term>
<listitem>
<para>
Force FTP downloads to use ASCII mode; normally, they use
binary mode. If you're downloading text documents, ASCII
mode will take care of any necessary conversions between
the text formats of the server and your computer.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--referer <replaceable>string</replaceable></term>
<term>-e <replaceable>string</replaceable></term>
<listitem>
<para>
Provides a ``referer'' string to the Web server.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--interface <replaceable>interface</replaceable></term>
<term>--n <replaceable>interface</replaceable></term>
<listitem>
<para>
Names a specific network interface to use (e.g., eth0 for
the first Ethernet interface on a Linux system). Rarely
needed.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>--headers</term>
<term>-h</term>
<listitem>
<para>
When performing an HTTP retrieval, include the HTTP message
header in the saved file. This is only really useful for
testing.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Files</title>
<variablelist>
<varlistentry>
<term><filename>~/.wmgetrc</filename></term>
<listitem>
<para>
The (optional) configuration file for the wmget dockapp.
Settings in this file are used to specify defaults for the
dockapp when it starts; see the section on configuration and
command-line options for more details.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>~/.wmget.iq</filename></term>
<listitem>
<para>
A Unix-domain socket created by the wmget dockapp to accept
requests from wmget commands. Created at startup
automatically.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
</refentry>

129
wmget/wmget.xpm Normal file
View file

@ -0,0 +1,129 @@
/* XPM */
static char * wmget_xpm[] = {
"240 120 6 1",
" c None",
". c #202020",
"+ c #000000",
"@ c #C7C3C7",
"# c #20B2AE",
"$ c #666666",
" . ",
" . ",
" . ",
" . ",
" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ . ",
" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@########################################################+@ . ",
" +@+++++++++++++++++++++++++++++++++++++++++++++++++++++++++@ . ",
" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ . ",
" . ",
" . ",
" . ",
" . ",
" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ . ",
" . ",
" . ",
" . ",
" . ",
" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@ ",
" +..........................................................@ . @########################################################+ @$$$$$$$$$$$$$$$+ @$$$$$$$$$$$$$$$+ ",
" +..........................................................@ . @########################################################+ @$$$$$$++$++$$$$$$+@$$$$$$+++++$$$$$$+ ",
" +..........................................................@ . @########################################################+ @$$$$$$+@$+@$$$$$$+@$$$$$$+$$$@$$$$$$+ ",
" +..........................................................@ . @########################################################+ @$$$$$$+@$+@$$$$$$+@$$$$$$+$$$@$$$$$$+ ",
" +..........................................................@ . @########################################################+ @$$$$$$+@$+@$$$$$$+@$$$$$$+$$$@$$$$$$+ ",
" +..........................................................@ . @########################################################+ @$$$$$$@@$@@$$$$$$+@$$$$$$@@@@@$$$$$$+ ",
" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ . @########################################################+ +$$$$$$$$$$$$$$$+ +$$$$$$$$$$$$$$$+ ",
" . @+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++ +++++++++++++++ ",
" . ",
" . .......................................................... ",
" . .......................................................... ",
" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . .......................................................... ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +..........................................................@ . ",
" +@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ . ",
" . ",
" . ",
" . ",
" . ",
"................................................................. ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
"+++#+++####+++###++####++#####+#####++###++#+++#++###++++###+#+++#+#+++++#+++#+#+++#++###++####+++###++####+++###++#####+#+++#+#+++#+#+++#+#+++#+#+++#+#####+ ",
"++#+#+++#++#+#+++#++#++#+#+++++#+++++#+++#+#+++#+++#+++++++#+#++#++#+++++#+++#+#+++#+#+++#+#+++#+#+++#+#+++#+#+++#+++#+++#+++#+#+++#+#+++#+#+++#+#+++#+++++#+ ",
"+#+++#++#++#+#++++++#++#+#+++++#+++++#+++++#+++#+++#+++++++#+#+#+++#+++++##+##+##++#+#+++#+#+++#+#+++#+#+++#+#+++++++#+++#+++#+#+++#+#+++#++#+#+++#+#+++++#++ ",
"+#+++#++###++#++++++#++#+####++####++#+++++#####+++#+++++++#+##++++#+++++#+#+#+#+#+#+#+++#+####++#+++#+####+++###++++#+++#+++#++#+#++#+#+#+++#+++++#+++++#+++ ",
"+#####++#++#+#++++++#++#+#+++++#+++++#++##+#+++#+++#+++++++#+#+#+++#+++++#+++#+#++##+#+++#+#+++++#+#+#+#+#+++++++#+++#+++#+++#++#+#++#+#+#++#+#++++#++++#++++ ",
"+#+++#++#++#+#+++#++#++#+#+++++#+++++#+++#+#+++#+++#+++#+++#+#++#++#+++++#+++#+#+++#+#+++#+#+++++#++#++#++#++#+++#+++#+++#+++#++#+#++##+##+#+++#+++#+++#+++++ ",
"+#+++#+####+++###++####++#####+#++++++###++#+++#++###+++###++#+++#+#####+#+++#+#+++#++###++#++++++##+#+#+++#++###++++#++++###++++#+++#+++#+#+++#+++#+++#####+ ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
" ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
"+++++++#+++++++++++++++#+++++++++##++++++++#+++++++#++++++#++#++++++##++++++++++++++++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++++++++++ ",
"+++++++#+++++++++++++++#++++++++#++#+++++++#+++++++++++++++++#+++++++#++++++++++++++++++++++++++++++++++++++++++++++#++++++++++++++++++++++++++++++++++++++++ ",
"++###++#+##+++###+++##+#++###+++#+++++##+#+#+##+++##+++++##++#+++#+++#+++##+#++#+##+++###++#+##+++##+#+#+##+++###++####++#+++#+#+++#+#+++#+#+++#+#+++#+#####+ ",
"+++++#+##++#+#+++#+#++##+#+++#+####++#++#++##++#+++#++++++#++#++#++++#+++#+#+#+##++#+#+++#+##++#+#++##+##++#+#++++++#++++#+++#+#+++#+#+++#++#+#++#+++#++++#++ ",
"++####+#+++#+#+++++#+++#+#####++#+++++##+++#+++#+++#+++#++#++###+++++#+++#+#+#+#+++#+#+++#+##++#+#++##+#++++++###+++#++++#+++#++#+#++#+#+#+++#++++####+++#+++ ",
"+#+++#+##++#+#+++#+#++##+#++++++#+++++++#++#+++#+++#+++#++#++#++#++++#+++#+#+#+#+++#+#+++#+#+##+++##+#+#+++++++++#++#++#+#++##++#+#++#+#+#++#+#++++++#++#++++ ",
"++####+#+##+++###+++##+#++###+++#++++###+++#+++#++###+++##+++#+++#++###++#+++#+#+++#++###++#+++++++++#+#+++++####++++##+++##+#+++#++++#+#++#+++#++###++#####+ ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
" ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
"+++++++++#++++#+#+++#+#++++#++++#++#++#++++++##+++++#+++#++++++++++++++++++++++++++++++++++++++#+++#+++++#++++###++#####++++#++#####+++##++#####++###+++###++ ",
"+++++++++#++++#+#+++#+#+++###++#+#+#+#+#+++++#+++++#+++++#+++#+++#+++#+++++++++++++++++++++++++#++#+#+++##+++#+++#+++++#+++##++#++++++#++++++++#+#+++#+#+++#+ ",
"+++++++++#++++#+#++#####+#+#++++#+#++#+#++++#+++++#+++++++#+++#+#++++#++++++++++++++++++++++++#++#+++#+#+#+++++++#++++#+++#+#++#+##++#++++++++#++#+++#+#++##+ ",
"+++++++++#++++++++++#+#+++###++++#++++#+++++++++++#+++++++#++#####+#####+++++++#####+++++++++#+++#+++#+++#+++++##++++##++#++#++##++#+#+##+++++#+++###+++##+#+ ",
"+++++++++#+++++++++#####+++#+#++#+#++#+#+#++++++++#+++++++#+++#+#++++#+++++##++++++++++#++++#++++#+++#+++#++++#++++++++#+#####+++++#+##++#+++#+++#+++#+++++#+ ",
"++++++++++++++++++++#+#+++###++#+#+#+#++#++++++++++#+++++#+++#+++#+++#+++++#++++++++++###++#++++++#+#++++#+++#+++++#+++#++++#++#+++#+#+++#++#++++#+++#++++#++ ",
"+++++++++#++++++++++#+#++++#+++#++#+++##+#++++++++++#+++#+++++++++++++++++#++++++++++++#+++#+++++++#+++#####+#####++###+++++#+++###+++###+++#+++++###+++##+++ ",
"+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ",
" ",
" ",
" ",
" ",
" ",
" ",
" "};