Convers all text files to LF instead of CRLF.
This commit is contained in:
parent
6e02e322a6
commit
f2caae393d
15 changed files with 1572 additions and 1799 deletions
3
Makefile
3
Makefile
|
@ -6,6 +6,3 @@ loop: loop.asd loop.lisp scripts/build-exe.lisp
|
||||||
install: loop
|
install: loop
|
||||||
mkdir -p `head -1 conf-home` && \
|
mkdir -p `head -1 conf-home` && \
|
||||||
cp -R loop accounts.lisp groups scripts `head -1 conf-home`
|
cp -R loop accounts.lisp groups scripts `head -1 conf-home`
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.pdf *.out *.aux *.log *.fls *.fdb_latexmk loop loop.tex
|
|
||||||
|
|
306
README
306
README
|
@ -1,153 +1,153 @@
|
||||||
(*) Introduction
|
(*) Introduction
|
||||||
|
|
||||||
LOOP is an NNTP server written in Common Lisp.
|
LOOP is an NNTP server written in Common Lisp.
|
||||||
|
|
||||||
(*) Assumptions
|
(*) Assumptions
|
||||||
|
|
||||||
We assume
|
We assume
|
||||||
|
|
||||||
- you run SBCL
|
- you run SBCL
|
||||||
|
|
||||||
- you have Quicklisp installed and knows how to use it
|
- you have Quicklisp installed and knows how to use it
|
||||||
|
|
||||||
- you know how to use a TCP server such as
|
- you know how to use a TCP server such as
|
||||||
|
|
||||||
https://cr.yp.to/ucspi-tcp.html
|
https://cr.yp.to/ucspi-tcp.html
|
||||||
|
|
||||||
- you know how to manage a daemon with
|
- you know how to manage a daemon with
|
||||||
|
|
||||||
https://cr.yp.to/daemontools.html
|
https://cr.yp.to/daemontools.html
|
||||||
|
|
||||||
- you have git and knows how to use it
|
- you have git and knows how to use it
|
||||||
|
|
||||||
(*) How to install it
|
(*) How to install it
|
||||||
|
|
||||||
LOOP is not in the Quicklisp repository, so we'll instruct you to
|
LOOP is not in the Quicklisp repository, so we'll instruct you to
|
||||||
install it as a local project. Go to
|
install it as a local project. Go to
|
||||||
|
|
||||||
~/quicklisp/local-projects/
|
~/quicklisp/local-projects/
|
||||||
|
|
||||||
and say
|
and say
|
||||||
|
|
||||||
$ git clone https://git.antartida.xyz/loop/srv loop
|
$ git clone https://git.antartida.xyz/loop/srv loop
|
||||||
$ cd loop
|
$ cd loop
|
||||||
$ make
|
$ make
|
||||||
|
|
||||||
If you just installed SBCL and quicklisp, the build might take a
|
If you just installed SBCL and quicklisp, the build might take a
|
||||||
little while to download dependencies. Be patient. Now you have the
|
little while to download dependencies. Be patient. Now you have the
|
||||||
executable loop. Try it out:
|
executable loop. Try it out:
|
||||||
|
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
$ ./loop --help
|
$ ./loop --help
|
||||||
NAME:
|
NAME:
|
||||||
loop - An NNTP server for a circle of friends.
|
loop - An NNTP server for a circle of friends.
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
loop [options] [arguments ...]
|
loop [options] [arguments ...]
|
||||||
|
|
||||||
OPTIONS:
|
OPTIONS:
|
||||||
--change-passwd <VALUE> <username> <new-password> changes password
|
--change-passwd <VALUE> <username> <new-password> changes password
|
||||||
--create-account <VALUE> <username> <invited-by> creates a new account
|
--create-account <VALUE> <username> <invited-by> creates a new account
|
||||||
--help display usage information and exit
|
--help display usage information and exit
|
||||||
--logging turn on debug logging on stderr
|
--logging turn on debug logging on stderr
|
||||||
--version display version and exit
|
--version display version and exit
|
||||||
-l, --list-accounts lists accounts
|
-l, --list-accounts lists accounts
|
||||||
-r, --repl run a REPL on port 4006
|
-r, --repl run a REPL on port 4006
|
||||||
-s, --server runs NNTP server reading from stdout
|
-s, --server runs NNTP server reading from stdout
|
||||||
|
|
||||||
AUTHORS:
|
AUTHORS:
|
||||||
Circling Skies <loop@antartida.xyz>
|
Circling Skies <loop@antartida.xyz>
|
||||||
|
|
||||||
LICENSE:
|
LICENSE:
|
||||||
GPL v3
|
GPL v3
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
|
|
||||||
You can talk to the NNTP server with -s:
|
You can talk to the NNTP server with -s:
|
||||||
|
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
$ ./loop -s
|
$ ./loop -s
|
||||||
200 Welcome! Say ``help'' for a menu.
|
200 Welcome! Say ``help'' for a menu.
|
||||||
quit
|
quit
|
||||||
205 Good-bye.
|
205 Good-bye.
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
|
|
||||||
It's time to choose a directory from which LOOP will run. Say you
|
It's time to choose a directory from which LOOP will run. Say you
|
||||||
choose $HOME/loop. Then say
|
choose $HOME/loop. Then say
|
||||||
|
|
||||||
$ echo $HOME/loop > conf-home
|
$ echo $HOME/loop > conf-home
|
||||||
$ make install
|
$ make install
|
||||||
|
|
||||||
Whenever you run loop, make sure you're in its home directory because
|
It's time to create an account you. Whenever you run loop, make sure
|
||||||
it will look for the file accounts.lisp always relatively to the
|
you're in its home directory because it will look for the file
|
||||||
current working directory of the process. (So, if you set up a cron
|
accounts.lisp always relatively to the current working directory of
|
||||||
job, make sure the job, too, sets LOOP's home directory as its current
|
the process. (So, if you set up a cron job later on, make sure the
|
||||||
working directory.)
|
job, too, sets LOOP's home directory as its current working
|
||||||
|
directory.)
|
||||||
(*) Create your account
|
|
||||||
|
(*) Create your account
|
||||||
LOOP requires authentication for most things, so you should create an
|
|
||||||
account for you right away. Accounts are kept in accounts.lisp in
|
LOOP requires authentication for most things, so you should create an
|
||||||
your installation directory. Every time you create an account, you
|
account for you right away. Accounts are kept in accounts.lisp in
|
||||||
must specify who is inviting this new account into the LOOP---because
|
your installation directory. Every time you create an account, you
|
||||||
we keep a tree of accounts. The root account is called anonymous, so
|
must specify who is inviting this new account into the LOOP---because
|
||||||
your first account must be invited by the anonymous account. So you
|
we keep a tree of accounts. The root account is called anonymous, so
|
||||||
can say
|
your first account must be invited by the anonymous account. So you
|
||||||
|
can say
|
||||||
./loop --create-account you anonymous
|
|
||||||
|
./loop --create-account you anonymous
|
||||||
The anonymous account has no special power. It exists solely because
|
|
||||||
the graph of accounts needs a root.
|
The anonymous account has no special power. It exists solely because
|
||||||
|
the graph of accounts needs a root.
|
||||||
(*) How to expose it to the network
|
|
||||||
|
(*) How to expose it to the network
|
||||||
Just have your system run your TCP server of choice. For instance, if
|
|
||||||
you're using djb's tcpserver and would like LOOP to listen on port
|
Run your TCP server of choice. For instance, if you're using djb's
|
||||||
1024, tell your shell
|
tcpserver and would like LOOP to listen on port 1024, tell your shell
|
||||||
|
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
$ tcpserver -v -HR 0.0.0.0 1024 ./loop -s
|
$ tcpserver -v -HR 0.0.0.0 1024 ./loop -s
|
||||||
tcpserver: status: 0/40
|
tcpserver: status: 0/40
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
|
|
||||||
Using another terminal, telnet to your host on port 1024:
|
Using another terminal, telnet to your host on port 1024:
|
||||||
|
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
$ telnet localhost 1024
|
$ telnet localhost 1024
|
||||||
Trying 127.0.0.1...
|
Trying 127.0.0.1...
|
||||||
Connected to antartida.xyz.
|
Connected to antartida.xyz.
|
||||||
Escape character is '^]'.
|
Escape character is '^]'.
|
||||||
200 Welcome! Say ``help'' for a menu.
|
200 Welcome! Say ``help'' for a menu.
|
||||||
quit
|
quit
|
||||||
205 Good-bye.
|
205 Good-bye.
|
||||||
Connection closed by foreign host.
|
Connection closed by foreign host.
|
||||||
--8<-------------------------------------------------------->8---
|
--8<-------------------------------------------------------->8---
|
||||||
|
|
||||||
Directories daemon/ and daemon-tls/ in LOOP's source code have sample
|
Directories daemon/ and daemon-tls/ in LOOP's source code have sample
|
||||||
scripts to use with djb's tcpserver and daemontools. If you have
|
scripts to use with djb's tcpserver and daemontools. If you have
|
||||||
never done this, it will be better education if you learn to use
|
never done this, it will be better education if you learn to use
|
||||||
daemontools and ucspi-tcp before going live with a LOOP community.
|
daemontools and ucspi-tcp before going live with a LOOP community.
|
||||||
It's easy and fun.
|
It's easy and fun.
|
||||||
|
|
||||||
(*) Systems with no installation issues
|
(*) Systems with no installation issues
|
||||||
|
|
||||||
We installed LOOP just fine on
|
We installed LOOP just fine on
|
||||||
|
|
||||||
FreeBSD 14.1, 14.2 with SBCL 2.4.9.
|
FreeBSD 14.1, 14.2 with SBCL 2.4.9.
|
||||||
Debian GNU/Linux 8.11 codename jessie with SBCL 1.2.4.debian.
|
Debian GNU/Linux 8.11 codename jessie with SBCL 1.2.4.debian.
|
||||||
|
|
||||||
(*) Systems with installation issues
|
(*) Systems with installation issues
|
||||||
|
|
||||||
We installed LOOP on Ubuntu 24.04 (24.01.1 LTS) codename noble with
|
We installed LOOP on Ubuntu 24.04 (24.01.1 LTS) codename noble with
|
||||||
SBCL 2.2.9.debian. We found that CLSQL could not load the shared
|
SBCL 2.2.9.debian. We found that CLSQL could not load the shared
|
||||||
object libsqlite3.so because ``apt install libsqlite3'' installs the
|
object libsqlite3.so because ``apt install libsqlite3'' installs the
|
||||||
library at
|
library at
|
||||||
|
|
||||||
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
|
/usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6
|
||||||
|
|
||||||
with a symbolic link to libsqlite3.so.0, but not to libsqlite3.so.
|
with a symbolic link to libsqlite3.so.0, but not to libsqlite3.so.
|
||||||
SBCL is trying to load libsqlite3.so, so a solution is to just tell
|
SBCL is trying to load libsqlite3.so, so a solution is to just tell
|
||||||
your system to
|
your system to
|
||||||
|
|
||||||
ln -s libsqlite3.so.0 libsqlite3.so
|
ln -s libsqlite3.so.0 libsqlite3.so
|
||||||
|
|
||||||
at /usr/lib/x86_64-linux-gnu.
|
at /usr/lib/x86_64-linux-gnu.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
In this service example, we're using tlswrapper by Jan Mojžíš, the
|
In this service example, we're using tlswrapper by Jan Mojžíš, the
|
||||||
source of which you can find at
|
source of which you can find at
|
||||||
|
|
||||||
https://github.com/janmojzis/tlswrapper
|
https://github.com/janmojzis/tlswrapper
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo loop-tls-logger
|
echo loop-tls-logger
|
||||||
exec /usr/bin/logger -i -t loop-tls
|
exec /usr/bin/logger -i -t loop-tls
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
echo loop-tls
|
echo loop-tls
|
||||||
cd /path/to/loop
|
cd /path/to/loop
|
||||||
exec tcpserver -HR 0.0.0.0 563 \
|
exec tcpserver -HR 0.0.0.0 563 \
|
||||||
/usr/bin/tlswrapper -f \
|
/usr/bin/tlswrapper -f \
|
||||||
/usr/local/etc/letsencrypt/live/mydomain/cert-priv1.pem \
|
/usr/local/etc/letsencrypt/live/mydomain/cert-priv1.pem \
|
||||||
/path/to/loop
|
/path/to/loop
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Date: 2024-03-07 21:44:31 GMT-3
|
Date: 2024-03-07 21:44:31 GMT-3
|
||||||
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
|
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
|
||||||
From: Loop
|
From: Loop
|
||||||
Subject: let there be light
|
Subject: let there be light
|
||||||
Newsgroups: local.control.news
|
Newsgroups: local.control.news
|
||||||
|
|
||||||
Administrative news will be posted here by me. -- Loop
|
Administrative news will be posted here by me. -- Loop
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Date: 2024-03-07 21:44:31 GMT-3
|
Date: 2024-03-07 21:44:31 GMT-3
|
||||||
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
|
Message-Id: <edjocyeqzqqhnswlbrbo@loop>
|
||||||
From: Loop
|
From: Loop
|
||||||
Subject: let there be light
|
Subject: let there be light
|
||||||
Newsgroups: local.test
|
Newsgroups: local.test
|
||||||
|
|
||||||
A sample group.
|
A sample group.
|
||||||
|
|
14
loop.asd
14
loop.asd
|
@ -1,7 +1,7 @@
|
||||||
;;; -*- Mode: LISP; Syntax: COMMON-LISP; -*-
|
;;; -*- Mode: LISP; Syntax: COMMON-LISP; -*-
|
||||||
(asdf:defsystem :loop
|
(asdf:defsystem :loop
|
||||||
:version "0.1"
|
:version "0.1"
|
||||||
:description "An NNTP server for a circle of friends."
|
:description "An NNTP server for a circle of friends."
|
||||||
:depends-on (:lisp-unit :str :uiop :cl-ppcre :local-time :clsql-sqlite3 :clingon
|
:depends-on (:lisp-unit :str :uiop :cl-ppcre :local-time :clsql-sqlite3 :clingon
|
||||||
:filesystem-utils)
|
:filesystem-utils)
|
||||||
:components ((:file "loop")))
|
:components ((:file "loop")))
|
||||||
|
|
26
loop.nw
26
loop.nw
|
@ -260,19 +260,19 @@ the response. You can get a string version of these numbers:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
LOOP> (bytes->string (response-data (dispatch (make-request :verb "HELP"))))
|
LOOP> (bytes->string (response-data (dispatch (make-request :verb "HELP"))))
|
||||||
"What's on the menu today?
|
"What's on the menu today?
|
||||||
ARTICLE fetches full articles
|
ARTICLE fetches full articles
|
||||||
AUTHINFO makes me trust you
|
AUTHINFO makes me trust you
|
||||||
BODY fetches an article body
|
BODY fetches an article body
|
||||||
GROUP sets the current group
|
GROUP sets the current group
|
||||||
HEAD fetches article headers
|
HEAD fetches article headers
|
||||||
HELP displays this menu
|
HELP displays this menu
|
||||||
LIST lists all groups
|
LIST lists all groups
|
||||||
MODE handles the mode request from clients
|
MODE handles the mode request from clients
|
||||||
NEXT increments the article pointer
|
NEXT increments the article pointer
|
||||||
POST posts your article
|
POST posts your article
|
||||||
QUIT politely says good-bye
|
QUIT politely says good-bye
|
||||||
XDD displays your state of affairs
|
XDD displays your state of affairs
|
||||||
XOVER fetches the overview database of a group"
|
XOVER fetches the overview database of a group"
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
|
224
peat
224
peat
|
@ -1,224 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf8 -*-
|
|
||||||
|
|
||||||
##############################
|
|
||||||
# ____ ___ ____ ______ #
|
|
||||||
# | \ / _] / T| T #
|
|
||||||
# | o )/ [_ Y o || | #
|
|
||||||
# | _/Y _]| |l_j l_j #
|
|
||||||
# | | | [_ | _ | | | #
|
|
||||||
# | | | T| | | | | #
|
|
||||||
# l__j l_____jl__j__j l__j #
|
|
||||||
# #
|
|
||||||
##### #####
|
|
||||||
# Repeat commands! #
|
|
||||||
##################
|
|
||||||
|
|
||||||
import errno, os, subprocess, sys, time
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
|
|
||||||
interval = 1.0
|
|
||||||
command = 'true'
|
|
||||||
clear = True
|
|
||||||
get_paths = lambda: set()
|
|
||||||
verbose = True
|
|
||||||
dynamic = None
|
|
||||||
last_run = None
|
|
||||||
|
|
||||||
|
|
||||||
USAGE = r"""usage: %prog [options] COMMAND
|
|
||||||
|
|
||||||
COMMAND should be given as a single argument using a shell string.
|
|
||||||
|
|
||||||
A list of paths to watch should be piped in on standard input.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
find . | peat './test.sh'
|
|
||||||
find . -name '*.py' | peat 'rm *.pyc'
|
|
||||||
find . -name '*.py' -print0 | peat -0 'rm *.pyc'
|
|
||||||
|
|
||||||
If --dynamic is used, the given command will be run each time to generate the
|
|
||||||
list of files to check:
|
|
||||||
|
|
||||||
peat --dynamic 'find .' './test.sh'
|
|
||||||
peat --dynamic 'find . -name '\''*.py'\''' 'rm *.pyc'
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def log(s):
|
|
||||||
if verbose:
|
|
||||||
print(s)
|
|
||||||
|
|
||||||
def die(s):
|
|
||||||
sys.stderr.write('ERROR: ' + s + '\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
def check(paths):
|
|
||||||
for p in paths:
|
|
||||||
try:
|
|
||||||
if os.stat(p).st_mtime >= last_run:
|
|
||||||
return True
|
|
||||||
except OSError as e:
|
|
||||||
# If the file has been deleted since we started watching, don't
|
|
||||||
# worry about it.
|
|
||||||
if e.errno == errno.ENOENT:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
return False
|
|
||||||
|
|
||||||
def run():
|
|
||||||
global last_run
|
|
||||||
last_run = time.time()
|
|
||||||
log("running: " + command)
|
|
||||||
subprocess.call(command, shell=True)
|
|
||||||
|
|
||||||
def build_option_parser():
|
|
||||||
p = OptionParser(USAGE)
|
|
||||||
|
|
||||||
# Main options
|
|
||||||
p.add_option('-i', '--interval', default=None,
|
|
||||||
help='interval between checks in milliseconds',
|
|
||||||
metavar='N')
|
|
||||||
p.add_option('-I', '--smart-interval', dest='interval',
|
|
||||||
action='store_const', const=None,
|
|
||||||
help='determine the interval based on number of files watched (default)')
|
|
||||||
|
|
||||||
p.add_option('-d', '--dynamic', default=None,
|
|
||||||
help='run COMMAND before each run to generate the list of files to check',
|
|
||||||
metavar='COMMAND')
|
|
||||||
p.add_option('-D', '--no-dynamic', dest='dynamic',
|
|
||||||
action='store_const', const=None,
|
|
||||||
help='take a list of files to watch on standard in (default)')
|
|
||||||
|
|
||||||
p.add_option('-c', '--clear', default=True,
|
|
||||||
action='store_true', dest='clear',
|
|
||||||
help='clear screen before runs (default)')
|
|
||||||
p.add_option('-C', '--no-clear',
|
|
||||||
action='store_false', dest='clear',
|
|
||||||
help="don't clear screen before runs")
|
|
||||||
|
|
||||||
p.add_option('-v', '--verbose', default=True,
|
|
||||||
action='store_true', dest='verbose',
|
|
||||||
help='show extra logging output (default)')
|
|
||||||
p.add_option('-q', '--quiet',
|
|
||||||
action='store_false', dest='verbose',
|
|
||||||
help="don't show extra logging output")
|
|
||||||
|
|
||||||
p.add_option('-w', '--whitespace', default=None,
|
|
||||||
action='store_const', dest='sep', const=None,
|
|
||||||
help="assume paths are separated by whitespace (default)")
|
|
||||||
p.add_option('-n', '--newlines',
|
|
||||||
action='store_const', dest='sep', const='\n',
|
|
||||||
help="assume paths are separated by newlines")
|
|
||||||
p.add_option('-s', '--spaces',
|
|
||||||
action='store_const', dest='sep', const=' ',
|
|
||||||
help="assume paths are separated by spaces")
|
|
||||||
p.add_option('-0', '--zero',
|
|
||||||
action='store_const', dest='sep', const='\0',
|
|
||||||
help="assume paths are separated by null bytes")
|
|
||||||
|
|
||||||
return p
|
|
||||||
|
|
||||||
|
|
||||||
def _main():
|
|
||||||
if dynamic:
|
|
||||||
log("Running the following command to generate watch list:")
|
|
||||||
log(' ' + dynamic)
|
|
||||||
log('')
|
|
||||||
|
|
||||||
log("Watching the following paths:")
|
|
||||||
for p in get_paths():
|
|
||||||
log(' ' + p)
|
|
||||||
log('')
|
|
||||||
log('Checking for changes every %d milliseconds.' % int(interval * 1000))
|
|
||||||
log('')
|
|
||||||
|
|
||||||
run()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
time.sleep(interval)
|
|
||||||
if check(get_paths()):
|
|
||||||
if clear:
|
|
||||||
subprocess.check_call('clear')
|
|
||||||
run()
|
|
||||||
|
|
||||||
def smart_interval(count):
|
|
||||||
"""Return the smart interval to use in milliseconds."""
|
|
||||||
if count >= 50:
|
|
||||||
return 1000
|
|
||||||
else:
|
|
||||||
sq = lambda n: n * n
|
|
||||||
return int(1000 * (1 - (sq(50.0 - count) / sq(50))))
|
|
||||||
|
|
||||||
def _parse_interval(options):
|
|
||||||
global get_paths
|
|
||||||
if options.interval:
|
|
||||||
i = int(options.interval)
|
|
||||||
elif options.dynamic:
|
|
||||||
i = 1000
|
|
||||||
else:
|
|
||||||
i = smart_interval(len(get_paths()))
|
|
||||||
|
|
||||||
return i / 1000.0
|
|
||||||
|
|
||||||
def _parse_paths(sep, data):
|
|
||||||
if not sep:
|
|
||||||
paths = data.split()
|
|
||||||
else:
|
|
||||||
paths = data.split(sep)
|
|
||||||
|
|
||||||
paths = [p.rstrip('\n') for p in paths if p]
|
|
||||||
paths = map(os.path.abspath, paths)
|
|
||||||
paths = set(paths)
|
|
||||||
|
|
||||||
return paths
|
|
||||||
|
|
||||||
def main():
|
|
||||||
global interval, command, clear, get_paths, verbose, dynamic
|
|
||||||
|
|
||||||
(options, args) = build_option_parser().parse_args()
|
|
||||||
|
|
||||||
if len(args) != 1:
|
|
||||||
die("exactly one command must be given")
|
|
||||||
|
|
||||||
command = args[0]
|
|
||||||
clear = options.clear
|
|
||||||
verbose = options.verbose
|
|
||||||
sep = options.sep
|
|
||||||
dynamic = options.dynamic
|
|
||||||
|
|
||||||
if dynamic:
|
|
||||||
def _get_paths():
|
|
||||||
data = subprocess.check_output(dynamic, shell=True)
|
|
||||||
return _parse_paths(sep, data)
|
|
||||||
|
|
||||||
get_paths = _get_paths
|
|
||||||
else:
|
|
||||||
data = sys.stdin.read()
|
|
||||||
paths = _parse_paths(sep, data)
|
|
||||||
|
|
||||||
if not paths:
|
|
||||||
die("no paths to watch were given on standard input")
|
|
||||||
|
|
||||||
for path in paths:
|
|
||||||
if not os.path.exists(path):
|
|
||||||
die('path to watch does not exist: ' + repr(path))
|
|
||||||
|
|
||||||
get_paths = lambda: paths
|
|
||||||
|
|
||||||
interval = _parse_interval(options)
|
|
||||||
|
|
||||||
_main()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
import signal
|
|
||||||
def sigint_handler(signal, frame):
|
|
||||||
sys.stdout.write('\n')
|
|
||||||
sys.exit(130)
|
|
||||||
signal.signal(signal.SIGINT, sigint_handler)
|
|
||||||
main()
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#-quicklisp
|
#-quicklisp
|
||||||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||||||
(user-homedir-pathname))))
|
(user-homedir-pathname))))
|
||||||
(when (probe-file quicklisp-init)
|
(when (probe-file quicklisp-init)
|
||||||
(load quicklisp-init)))
|
(load quicklisp-init)))
|
||||||
(ql:quickload :loop :silent t)
|
(ql:quickload :loop :silent t)
|
||||||
(sb-ext:save-lisp-and-die #P"loop"
|
(sb-ext:save-lisp-and-die #P"loop"
|
||||||
:toplevel #'loop:main
|
:toplevel #'loop:main
|
||||||
:executable t
|
:executable t
|
||||||
:save-runtime-options t)
|
:save-runtime-options t)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#-quicklisp
|
#-quicklisp
|
||||||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||||||
(user-homedir-pathname))))
|
(user-homedir-pathname))))
|
||||||
(when (probe-file quicklisp-init)
|
(when (probe-file quicklisp-init)
|
||||||
(load quicklisp-init)))
|
(load quicklisp-init)))
|
||||||
(ql:quickload :loop :silent t)
|
(ql:quickload :loop :silent t)
|
||||||
(in-package #:loop)
|
(in-package #:loop)
|
||||||
(connect-index! "message-id.db")
|
(connect-index! "message-id.db")
|
||||||
(drop-create-index!)
|
(drop-create-index!)
|
||||||
(index-from-fs!)
|
(index-from-fs!)
|
||||||
(format t "Index built.~%")
|
(format t "Index built.~%")
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
#-quicklisp
|
#-quicklisp
|
||||||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||||||
(user-homedir-pathname))))
|
(user-homedir-pathname))))
|
||||||
(when (probe-file quicklisp-init)
|
(when (probe-file quicklisp-init)
|
||||||
(load quicklisp-init)))
|
(load quicklisp-init)))
|
||||||
(ql:quickload :loop :silent t)
|
(ql:quickload :loop :silent t)
|
||||||
(in-package #:loop)
|
(in-package #:loop)
|
||||||
(read-accounts!)
|
(read-accounts!)
|
||||||
(connect-index! "message-id.db")
|
(connect-index! "message-id.db")
|
||||||
(remove-inactive-users!)
|
(remove-inactive-users!)
|
||||||
(write-accounts!)
|
(write-accounts!)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#-quicklisp
|
#-quicklisp
|
||||||
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
(let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp"
|
||||||
(user-homedir-pathname))))
|
(user-homedir-pathname))))
|
||||||
(when (probe-file quicklisp-init)
|
(when (probe-file quicklisp-init)
|
||||||
(load quicklisp-init)))
|
(load quicklisp-init)))
|
||||||
(ql:quickload :loop :silent t)
|
(ql:quickload :loop :silent t)
|
||||||
(in-package #:loop)
|
(in-package #:loop)
|
||||||
(migrate-add-creation-and-post-date!)
|
(migrate-add-creation-and-post-date!)
|
||||||
(format t "Accounts rewritten.~%")
|
(format t "Accounts rewritten.~%")
|
||||||
|
|
Loading…
Reference in a new issue