Add kiosk mode and drop SBCL - clic - Clic is an command line interactive client for gopher written in Common LISP
HTML git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/
DIR Log
DIR Files
DIR Refs
DIR Tags
DIR README
DIR LICENSE
---
DIR commit fb5155bf84450fec31f6b69a49fbdc07aee5aa70
DIR parent b76072a5f201bed263c85b2776dff7ed10b6aac1
HTML Author: Solene Rapenne <solene@perso.pw>
Date: Fri, 6 Apr 2018 10:08:01 +0200
Add kiosk mode and drop SBCL
Diffstat:
M Makefile | 17 +++++------------
M README.md | 38 +++++++++----------------------
D TODO | 14 --------------
M clic.1 | 2 +-
M clic.lisp | 88 ++++++++++++++++---------------
D extension.c | 17 -----------------
D interactive-test.exp | 116 ------------------------------
M make-binary.lisp | 16 ----------------
D run-test.sh | 14 --------------
D test.lisp | 35 -------------------------------
10 files changed, 62 insertions(+), 295 deletions(-)
---
DIR diff --git a/Makefile b/Makefile
@@ -2,7 +2,7 @@
# See the LICENSE file for copyright and license details.
.POSIX:
-VERSION = 0.1
+VERSION = 0.2
BIN = clic
LISP = ecl
@@ -10,17 +10,10 @@ PREFIX = /usr
BINDIR = ${PREFIX}/bin
MANDIR = ${PREFIX}/share/man
-all: extension.so ${BIN}
+all: ${BIN}
-${BIN}: clic.lisp make-binary.lisp
- ${LISP} --load make-binary.lisp
-
-standalone: clic.lisp extension.so make-binary.lisp
- ${MAKE} -e LISP=sbcl
-
-extension.so: extension.c
- ${CC} -Wall -fPIC -c extension.c
- ${LD} -shared -o extension.so extension.o
+${BIN}: clic.lisp make-binary.lisp
+ ecl -load make-binary.lisp
install: ${BIN}
@echo installing executable to "${DESTDIR}${PREFIX}/bin"
@@ -39,7 +32,7 @@ uninstall:
@rm -f ${DESTDIR}${MANDIR}/man1/clic.1
clean:
- rm -f "${BIN}" clic.o clic.eclh clic.cxx bookmark-test extension.so
+ rm -f "${BIN}" clic.o clic.eclh clic.cxx
test: clean all
@sh run-test.sh ${LISP}
DIR diff --git a/README.md b/README.md
@@ -12,46 +12,25 @@ Requirements
clic requires a few dependencies :
+ ANSI compatible terminal emulator
- + a Common LISP interpreter
+ + ecl common lisp interpreter
+ C compiler
+ Linux/OpenBSD/FreeBSD/NetBSD
-Both **ecl** and **sbcl** Common LISP compilers are supported.
-
How to build
============
`clic` binary must be compiled.
-To compile it with **ecl** :
+To compile it with **ecl**, it's really easy type the following
+command :
make
-To compile it with **sbcl** :
-
- make LISP=sbcl
-
then you can use `make install` to deploy it in `/usr/bin/`.
-Note : when using sbcl, a shared library extension.o is created and
-then sbcl creates a binary linked against the library. But ecl will
-translate the whole lisp code to C and then compile it, but linking
-against ecl.
-
-**I (the author) recommend using ecl**.
-
-
-Information about the binary
-----------------------------
-If you compile clic with ecl, you will need ecl library installed on
-the computer, the startup time is really fast. While compiling clic
-with SBCL will provide a standalone binary embedding the whole SBCL
-compiler, weighting approximately 10 Mb with a slower startup time.
-
-If you use OpenBSD and SBCL, you will need wxallowed mountflag on the
-partition from where you try to start clic standalone because sbcl has
-a W^X issue.
+The binary will be linked to ecl shared library. You need to install
+ecl if you want to deploy clic binary on others systems.
How to use clic
@@ -87,7 +66,12 @@ permitting to use clic with the numpad with only one hand :
Command line usage
==================
-clic [url|file]
+clic [-k] [url|file]
+
+If you start clic with -k parameter, then kiosk mode is enabled, which
+mean it won't call any external program or save any data on the
+disk. Texts (type 0) will be shown as-this in the output. It only
+allow to use texts, menus and searches.
If you pass a gopher url to clic (gopher:// isn't mandatory for the
url), the behavor will change depending on two parameters :
DIR diff --git a/TODO b/TODO
@@ -1,14 +0,0 @@
-* FEATURE
-
-- DONE add a bookmark "a" function to store the current page location
-- DONE add a bookmark "b" function to display the bookmarks
-- TODO add a dump "d" function to display the raw response
-- DONE history mode which save every file into a filesystem tree to preserve data if gopherhole go offline
-
-
-* CODE
-
-- TODO remove the pagination if using stdout or not in shell
-- TODO make the c-termsize working on ecl
-- DONE use CLOS to store data
-- DONE store the whole page and deal with it later
DIR diff --git a/clic.1 b/clic.1
@@ -3,7 +3,7 @@
clic \- a terminal gopher client
.SH SYNOPSIS
.B clic
-.IR [URL]
+.IR [-k] [URL]
.PP
.SH DESCRIPTION
.B clic
DIR diff --git a/clic.lisp b/clic.lisp
@@ -1,23 +1,10 @@
;;; let's hide the loading
(let ((*standard-output* (make-broadcast-stream)))
(require 'asdf)
- #+sbcl
- (require 'sb-bsd-sockets)
#+ecl
(require 'sockets))
;;;; C binding to get terminal informations
-;;;; SBCL only
-#+sbcl
-(progn
- (load-shared-object #p"./extension.so")
- ;; getTerminalHeight
- (declaim (inline getTerminalHeight))
- (sb-alien:define-alien-routine "getTerminalHeight" unsigned-int)
- (defun c-termsize ()
- "return terminal height"
- (sb-alien:with-alien ((res unsigned-int (getTerminalHeight))))))
-
#+ecl
(progn
(ffi:clines "
@@ -41,7 +28,7 @@
(defstruct location host port type uri
:predicate)
-;;;; kiosk mode on/off
+;;;; kiosk mode
(defparameter *kiosk-mode* nil)
(defmacro kiosk-mode(&body code)
@@ -98,8 +85,6 @@
"return t if the output is a terminal"
;; we use this variable in case we don't want to be interactive
;; like when we use a cmd arg to get an image
- #+sbcl
- (interactive-stream-p *standard-output*)
#+ecl
(if (= 1 (c-ttyp))
t
@@ -387,36 +372,44 @@
(defun parse-url(url)
"parse a gopher url and return a location"
- (let ((url (if (search "gopher://" url)
- (subseq url 9)
- url)))
+ (cond ((or
+ (string= "--help" url)
+ (string= "-h" url))
+ (help-shell)
+ (quit))
- ;; splitting with / to get host:port and uri
- ;; splitting host and port to get them
- (let* ((infos (split url #\/))
- (host-port (split (pop infos) #\:)))
+ ((string= "-k" url)
+ (setf *kiosk-mode* t))
- ;; create the location to visit
- (make-location :host (pop host-port)
-
- ;; default to port 70 if not supplied
- :port (if host-port ;; <- empty if no port given
- (parse-integer (car host-port))
- 70)
-
- ;; if type is empty we default to "1"
- :type (let ((type (pop infos)))
- (if (< 0 (length type)) type "1"))
+ (t
+
+ (let ((url (if (search "gopher://" url)
+ (subseq url 9)
+ url)))
+
+ ;; splitting with / to get host:port and uri
+ ;; splitting host and port to get them
+ (let* ((infos (split url #\/))
+ (host-port (split (pop infos) #\:)))
+
+ ;; create the location to visit
+ (make-location :host (pop host-port)
+ ;; default to port 70 if not supplied
+ :port (if host-port ;; <- empty if no port given
+ (parse-integer (car host-port))
+ 70)
+
+ ;; if type is empty we default to "1"
+ :type (let ((type (pop infos)))
+ (if (< 0 (length type)) type "1"))
- ;; glue remaining args between them
- :uri (format nil "~{/~a~}" infos)))))
+ ;; glue remaining args between them
+ :uri (format nil "~{/~a~}" infos)))))))
(defun get-argv()
"Parse argv and return it"
- #+sbcl
- (cadr *posix-argv*)
#+ecl
- (car (last (cdr (si::command-args)))))
+ (cdr (si::command-args)))
(defun user-input(input)
(cond
@@ -526,11 +519,13 @@
do
(formatted-output line)
+
;; split and ask to scroll or to type a command
(when (= row rows)
(setf row 0)
- (format t "~a press enter or a shell command ~a : "
+ (format t "~a~a press enter or a shell command ~a : "
(get-color 'bg-black)
+ (if *kiosk-mode* "KIOSK" "")
(get-color 'reset))
(force-output)
(let ((first-input (read-char *standard-input* nil nil t)))
@@ -659,7 +654,8 @@
(defun display-prompt()
(let ((last-page (car *history*)))
- (format t "gopher://~a:~a/~a~a (~as) / (p)rev (r)edisplay (h)istory : "
+ (format t "~agopher://~a:~a/~a~a (~as) / (p)rev (r)edisplay (h)istory : "
+ (if *kiosk-mode* "KIOSK " "")
(location-host last-page)
(location-port last-page)
(location-type last-page)
@@ -707,8 +703,14 @@
:type "1"
:uri argv))
;; it's not a file, create a location
- (parse-url argv))
- (make-location :host "gopherproject.org" :port 70 :uri "/" :type "1")))))
+ ;; it's either a list with parameters or a location
+ (if (listp argv)
+ (car (last (loop for element in argv collect (parse-url element))))
+ (parse-url argv)))))))
+
+ ;; if we didn't passed a url as parameter, use a default
+ (if (not (location-p destination))
+ (setf destination (make-location :host "gopherproject.org" :port 70 :uri "/" :type "1")))
;; is there an output redirection ?
(if (ttyp)
DIR diff --git a/extension.c b/extension.c
@@ -1,17 +0,0 @@
-#include <limits.h>
-#include <sys/ioctl.h>
-#include <unistd.h>
-
-/**
- * @brief Get the height of the terminal in term of visible lines
- * @return the height of the terminal or UINT_MAX in case of error
- */
-unsigned int getTerminalHeight()
-{
- struct winsize w;
- if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) < 0) {
- return UINT_MAX;
- }
-
- return w.ws_row;
-}
DIR diff --git a/interactive-test.exp b/interactive-test.exp
@@ -1,116 +0,0 @@
-proc user_input {} {
-
- expect "clic => "
-
- send "garbage\n"
- expect "clic => "
- send ". . ~. ~ .\n"
- expect "clic => "
-
- send "r\n"
- expect "clic => "
-
- send "help\n"
- expect "clic => "
-
- send "5\n"
- expect "clic => "
-
- send "p\n"
- expect "clic => "
-
- send "19\n"
- expect " : "
- send "\nr\n"
- expect " : "
-
- send "p\n"
- expect "clic => "
-
- send "r\n"
- expect "clic => "
-
- send "h\n"
- expect "clic => "
-
- send "a\n"
- expect "clic => "
-
- send "b\n"
- expect "clic => "
-}
-
-spawn ./clic gopher://bitreich.org
-set running [user_input]
-send "x\n"
-expect eof
-
-
-spawn sbcl
-expect "* "
-
-send "(require :sb-cover) (require :sb-bsd-sockets)\n"
-expect "* "
-
-send "(declaim (optimize sb-cover:store-coverage-data))\n"
-expect "* "
-
-send "(compile-file \"clic.lisp\")\n"
-expect "* "
-
-send "(load \"clic.fasl\")\n"
-expect "* "
-
-send "(setf *offline* t)\n"
-expect "* "
-
-send "(main)\n"
-set running [user_input]
-send "(pop *history*) (p) (r)\n"
-expect "* "
-
-send "19\n"
-expect " : "
-send "q\n"
-expect "* "
-
-send "(main)\n"
-expect "clic => "
-
-send "exit\n"
-expect "* "
-
-
-# add an argv to test argv parsing
-send "(setf *posix-argv* '(\"sbcl\" \"gopher://bitreich.org/0/documents/bitreich-manifesto.md\"))\n"
-expect "* "
-
-send "(main)\n"
-expect " : "
-send "q\n"
-expect "* "
-
-
-# add an argv to test argv parsing
-send "(setf *posix-argv* '(\"sbcl\" \"bitreich.org/1/usr/solene/\"))\n"
-expect "* "
-send "(main)\n"
-expect "clic => "
-send "q\n"
-expect "* "
-
-
-# add an argv to test argv parsing
-send "(setf *posix-argv* '(\"sbcl\" \"bitreich.org:70/\"))\n"
-expect "* "
-send "(main)\n"
-expect "clic => "
-send "q\n"
-expect "* "
-
-
-send "(sb-cover:report \"report/\")\n"
-expect "* "
-
-send "(quit)\n"
-expect eof
DIR diff --git a/make-binary.lisp b/make-binary.lisp
@@ -1,6 +1,4 @@
;; ecl produces a linked binary to ecl shared library
-;; sbcl produces a static binary (~ 10Mb with compression / 70Mb without)
-
(require 'asdf)
#+ecl
(require 'cmp)
@@ -10,20 +8,6 @@
(progn
(compile-file "clic.lisp" :system-p t)
(c:build-program "clic" :epilogue-code '(progn (handler-case (main) (condition () (quit)))) :lisp-files '("clic.o")))
-#+sbcl
-(progn
- (require 'sb-bsd-sockets)
- (sb-ext:disable-debugger)
- (load "clic.lisp")
- #+sb-core-compression
- (sb-ext:save-lisp-and-die "clic"
- :executable t
- :compression 5
- :toplevel 'main)
- #-sb-core-compression
- (sb-ext:save-lisp-and-die "clic"
- :executable t
- :toplevel 'main))
(format t "INFO => Compilation done (or at least it should be)~%")
(quit)
DIR diff --git a/run-test.sh b/run-test.sh
@@ -1,14 +0,0 @@
-#!/bin/sh
-
-LISP=$1
-
-expect -f interactive-test.exp
-
-${LISP} --load clic.lisp --load test.lisp
-
-./clic gopher://bitreich.org:70/0/ | md5sum -
-./clic bitreich.org:70/0/ | md5sum -
-./clic bitreich.org/0/ | md5sum -
-echo "/" | nc bitreich.org 70 | md5sum -
-
-
DIR diff --git a/test.lisp b/test.lisp
@@ -1,35 +0,0 @@
-;; we can write scenario here
-
-(print (parse-url "gopher://perso.pw:70/0/"))
-(print (parse-url "gopher://perso.pw/0/a"))
-(print (parse-url "perso.pw/0/some/uri.txt"))
-(print (parse-url "perso.pw"))
-(print (parse-url "perso.pw:70"))
-
-
-(setf *bookmark-file* "bookmark-test")
-(load-bookmark)
-(p)
-(g 1)
-(add-bookmark)
-(getpage "bitreich.org" 70 "/")
-(display-buffer "1")
-(g 7) ;; going to radio
-(g 1) ;; going back
-(g 21) ;; banana !
-(p) ;; going back
-(g 15)
-(g 1)
-(p)
-(p)
-(add-bookmark)
-(show-bookmarks)
-(g 1)
-
-(print *links*)
-
-(print *history*)
-(format t "~%")
-
-
-(quit)