Add kiosk mode and drop SBCL - clic - Clic is an command line interactive client for gopher written in Common LISP
 (HTM) 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
 (HTM) 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)