Commit 1784c870 authored by mbionchi's avatar mbionchi

shared object update:

  - allow for hotloading dynamic libs instead of textfiles
  - add guestbook dynamic library example
parent 7cf7cfb8
......@@ -5,19 +5,17 @@ TELNET-SITE
DESCRIPTION
-----------
telnet-site - a glorified text file reader intented to be used as a telnet site.
telnet-site - a glorified file reader intented to be used as a telnet site.
The telnet-site executable takes one argument, which is the path to the directory
that is the root of the site and contains text files to be displayed.
The telnet-site executable takes the following parameters:
The text files can be marked up with a minimal, non-intrusive syntax that will
allow telnet-site to do things such as flowing paragraphs and indenting list items.
The syntax is described in the FORMATTING section.
--site <path-to-dir> specify the path to the directory with site files
Furthermore, the text files can specify animations, as described in the
ANIMATIONS section.
--splash <path-to-file> specify the path to the splash screen file (optional)
Both the text window as well as the file index window are scrollable.
The files in the --site directory can either be plaintext with some formatting
(see the FORMATTING and ANIMATIONS sections) or shared objects specifying special
functions to work with the site(see the DYNAMIC LIBRARIES section).
BUILDING
......@@ -29,7 +27,7 @@ and then do the normal `./configure && make && make install` with your favourite
flags.
Otherwise, you can run
gcc -o telnetsite -lncurses ./src/*.c
gcc -o telnetsite -lncurses -ldl -Isrc ./src/*.c
SERVING OVER TELNET
......@@ -52,7 +50,7 @@ mytelnetsite stream tcp nowait <user> /usr/sbin/tcpd /usr/sbin/telnetd -h -E <pa
with the following:
#!/bin/sh
exec <path-to-binary> <path-to-site.d>
exec <path-to-binary> --site <path-to-site.d> [--splash <path-to-splash-file>]
where <path-to-binary> is the path to the telnet-site executable, and
<path-to-site.d> is the path to the root directory of the site.
......@@ -111,6 +109,44 @@ loading... -
loading... \
;loop
DYNAMIC LIBRARIES
-----------------
In order to allow for more interactive functionality (such as user input or
programmable animations), telnet-site can dynamically load shared objects it
finds in the --site path. The shared object files must end in .so in order for
the program to recognize it as such. The "src/module.h" file contains the
preprocessor macro definitions for function names that the shared object must
define in order to interoperate with the telnet-site:
void INIT_FUNC_NAME(WINDOW *) - mandatory, called on the initial load of the
shared object. The WINDOW pointer points to
the section of the screen the module is supposed
to print on.
void SCROLL_FUNC_NAME(int) - optional, called when the main input loop gets a
command to scroll up (-1) or down (1).
void SETMODE_FUNC_NAME(enum mode) - optional, called when the main input loop
detects mode change (COMMAND to INSERT or
the other way around). Used for custom user
input.
void GETCH_FUNC_NAME(int) - optional, called when getch() in the main input loop
returns during INSERT mode.
void KILL_FUNC_NAME() - optional, called when the content is about to go out of
focus. Is expected to clean up whatever memory was allocated
during the module's life.
You can find an example that uses all of the features above to implement a
simple guestbook in the examples/guestbook.c file.
To compile the example module as a shared library, you will need to:
gcc -g -o guestbook.so -Isrc -fpic -shared examples/guestbook.c src/*.c
BUGS
----
......
AC_INIT([telnet-site], [0.4], [https://gitlab.com/mb])
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS([config.h])
AC_PROG_CC
AC_PROG_CC_STDC
AC_CONFIG_FILES([Makefile src/Makefile])
AC_ARG_ENABLE([follow-links],
AS_HELP_STRING([--disable-follow-links], [Disable following symbolic links when reading site sections]))
AS_IF([test "x$enable_follow_links" != "xno"], [
AC_DEFINE([FOLLOW_LINKS], [], [Do not follow links when traversing directories])
])
PKG_CHECK_MODULES([ncurses], [ncurses])
AC_OUTPUT
This diff is collapsed.
bin_PROGRAMS = telnetsite
telnetsite_SOURCES = main.c anim.c anim.h site.c site.h splash.c splash.h data.c data.h scroll.c scroll.h render.c render.h log.c log.h
telnetsite_LDADD = ${ncurses_LIBS}
telnetsite_SOURCES = main.c anim.c anim.h site.c site.h splash.c splash.h data.c data.h scroll.c scroll.h render.c render.h log.c log.h module.h
telnetsite_LDADD = ${ncurses_LIBS} -ldl
AM_CFLAGS = -Isrc/
......@@ -22,102 +22,114 @@
#include <stddef.h>
void free_anim_refs(struct window *window) {
struct anim_ref *iter = window->content.anim_refs;
while (iter != NULL) {
struct anim_ref *tmp = iter;
iter = iter->next;
free(tmp);
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *iter = window->content.lines->anim_refs;
while (iter != NULL) {
struct anim_ref *tmp = iter;
iter = iter->next;
free(tmp);
}
window->content.lines->anim_refs = NULL;
}
window->content.anim_refs = NULL;
}
void pop_anim_ref_front(struct window *window) {
struct anim_ref *old = window->content.anim_refs;
if (old != NULL) {
window->content.anim_refs = window->content.anim_refs->next;
free(old);
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *old = window->content.lines->anim_refs;
if (old != NULL) {
window->content.lines->anim_refs = window->content.lines->anim_refs->next;
free(old);
}
}
}
void pop_anim_ref_back(struct window *window) {
struct anim_ref *iter = window->content.anim_refs,
*prev = NULL;
if (iter != NULL) {
prev = iter;
while (iter->next != NULL) {
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *iter = window->content.lines->anim_refs,
*prev = NULL;
if (iter != NULL) {
prev = iter;
iter = iter->next;
while (iter->next != NULL) {
prev = iter;
iter = iter->next;
}
prev->next = NULL;
free(iter);
}
prev->next = NULL;
free(iter);
}
}
void push_anim_ref_front(struct window *window, size_t index) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = window->content.anim_refs;
window->content.anim_refs = ref;
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = window->content.lines->anim_refs;
window->content.lines->anim_refs = ref;
}
}
void push_anim_ref_back(struct window *window, size_t index) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = NULL;
if (window->content.anim_refs == NULL) {
window->content.anim_refs = ref;
} else {
struct anim_ref *iter = window->content.anim_refs;
while (iter->next != NULL) {
iter = iter->next;
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *ref = malloc(sizeof(struct anim_ref));
ref->index = index;
ref->counter = 1;
ref->running = 1;
ref->next = NULL;
if (window->content.lines->anim_refs == NULL) {
window->content.lines->anim_refs = ref;
} else {
struct anim_ref *iter = window->content.lines->anim_refs;
while (iter->next != NULL) {
iter = iter->next;
}
iter->next = ref;
}
iter->next = ref;
}
}
void anim_tick(struct window *window) {
struct anim_ref *iter = window->content.anim_refs;
while (iter != NULL) {
if (iter->counter >= window->content.formatted[iter->index]->anim->frames[window->content.formatted[iter->index]->anim->current_frame_index].delay) {
if (iter->running) {
size_t i = iter->index;
size_t y = i-window->scroll;
if (window->content.formatted[i]->anim->current_frame_index ==
window->content.formatted[i]->anim->n_frames-1 &&
!window->content.formatted[i]->anim->loop) {
iter->running = 0;
} else {
scrollok(window->window, 0);
window->content.formatted[i]->anim->current_frame_index =
(window->content.formatted[i]->anim->current_frame_index + 1) %
window->content.formatted[i]->anim->n_frames;
mvwprintw(window->window, y, 0, "%s",
window->content.formatted[i]->anim->frames[window->content.formatted[i]->anim->current_frame_index].s->data);
i++;
y++;
while (i < window->content.n_formatted &&
window->content.formatted[i]->type == ANIM &&
!window->content.formatted[i]->anim->is_first_line) {
window->content.formatted[i]->anim->current_frame_index =
(window->content.formatted[i]->anim->current_frame_index + 1) %
window->content.formatted[i]->anim->n_frames;
if (window->content.type == STATIC && window->content.lines != NULL) {
struct anim_ref *iter = window->content.lines->anim_refs;
while (iter != NULL) {
if (iter->counter >= window->content.lines->formatted[iter->index]->anim->frames[window->content.lines->formatted[iter->index]->anim->current_frame_index].delay) {
if (iter->running) {
size_t i = iter->index;
size_t y = i-window->scroll;
if (window->content.lines->formatted[i]->anim->current_frame_index ==
window->content.lines->formatted[i]->anim->n_frames-1 &&
!window->content.lines->formatted[i]->anim->loop) {
iter->running = 0;
} else {
scrollok(window->window, 0);
window->content.lines->formatted[i]->anim->current_frame_index =
(window->content.lines->formatted[i]->anim->current_frame_index + 1) %
window->content.lines->formatted[i]->anim->n_frames;
mvwprintw(window->window, y, 0, "%s",
window->content.formatted[i]->anim->frames[window->content.formatted[i]->anim->current_frame_index].s->data);
window->content.lines->formatted[i]->anim->frames[window->content.lines->formatted[i]->anim->current_frame_index].s->data);
i++;
y++;
while (i < window->content.lines->n_formatted &&
window->content.lines->formatted[i]->type == ANIM &&
!window->content.lines->formatted[i]->anim->is_first_line) {
window->content.lines->formatted[i]->anim->current_frame_index =
(window->content.lines->formatted[i]->anim->current_frame_index + 1) %
window->content.lines->formatted[i]->anim->n_frames;
mvwprintw(window->window, y, 0, "%s",
window->content.lines->formatted[i]->anim->frames[window->content.lines->formatted[i]->anim->current_frame_index].s->data);
i++;
y++;
}
scrollok(window->window, 1);
wrefresh(window->window);
iter->counter = 1;
}
scrollok(window->window, 1);
wrefresh(window->window);
iter->counter = 1;
}
} else {
iter->counter++;
}
} else {
iter->counter++;
iter = iter->next;
}
iter = iter->next;
}
}
This diff is collapsed.
......@@ -18,12 +18,18 @@
#ifndef _TELNET_SITE_DATA_H
#define _TELNET_SITE_DATA_H
#define NO_TRAILING_NEWLINE 0x1
#include <stdio.h>
#include <dirent.h>
#include <curses.h>
char *binary_name;
enum content_type { STATIC, DYNAMIC };
enum mode { COMMAND, INSERT };
struct line {
char *line;
struct line *prev, *next;
......@@ -32,7 +38,7 @@ struct line {
struct section {
char *title;
char *filename;
struct line *content;
enum content_type type;
};
struct string {
......@@ -62,7 +68,7 @@ struct nline {
};
};
struct content {
struct static_content {
struct nline **raw;
size_t n_raw;
struct nline **formatted;
......@@ -70,6 +76,23 @@ struct content {
struct anim_ref *anim_refs;
};
struct dynamic_content {
void *so_handle;
void (*init_fun)(WINDOW *window);
void (*getch_fun)(int ch);
void (*scroll_fun)(int dy);
void (*setmode_fun)(enum mode mode);
void (*kill_fun)();
};
struct content {
enum content_type type;
union {
struct static_content *lines;
struct dynamic_content *dlib;
};
};
struct window {
WINDOW *window;
size_t cols, rows, scroll;
......@@ -84,10 +107,12 @@ struct anim_ref {
struct anim_ref *next;
};
size_t gen_err_opening(struct nline ***nlines);
void gen_err_opening(struct content *content);
int read_content_from_section(struct content *content, struct section *section);
struct nline *string2nline(char *str);
size_t read_nlines(FILE *fp, struct nline ***nlines);
size_t flow_nlines(struct nline **from, size_t n_from, struct nline ***to, int width);
size_t flow_nlines(struct nline **from, size_t n_from, struct nline ***to, int width, int options);
void print_nlines(struct nline **nlines, size_t nmemb);
void free_nlines(struct nline **nlines, size_t nmemb);
......@@ -102,7 +127,7 @@ size_t gen_index(struct section **sections, size_t nmemb, struct nline ***to, si
void print_lines(struct line *lines);
void dump_sections(struct section **sections, size_t n_sections);
struct section **read_sections(DIR *dir, char *dirname, int follow_links, size_t *nmemb);
struct section **read_sections(DIR *dir, char *dirname, size_t *nmemb);
struct line *flow_content(struct line *lines, int width);
void free_lines(struct line *lines);
void free_content(struct window *window);
......
......@@ -16,6 +16,7 @@
*/
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
......
/*
* This file is part of telnet-site.
*
* telnet-site is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* telnet-site is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef TELNET_SITE_MODULE_H
#define TELNET_SITE_MODULE_H
#define QUOTE(s) #s
#define TOSTRING(s) QUOTE(s)
#define INIT_FUNC_NAME module_init
#define SCROLL_FUNC_NAME module_scroll
#define SETMODE_FUNC_NAME module_setmode
#define GETCH_FUNC_NAME module_getch
#define KILL_FUNC_NAME module_kill
#define INIT_FUNC_NAME_S TOSTRING(INIT_FUNC_NAME)
#define SCROLL_FUNC_NAME_S TOSTRING(SCROLL_FUNC_NAME)
#define SETMODE_FUNC_NAME_S TOSTRING(SETMODE_FUNC_NAME)
#define GETCH_FUNC_NAME_S TOSTRING(GETCH_FUNC_NAME)
#define KILL_FUNC_NAME_S TOSTRING(KILL_FUNC_NAME)
#endif
......@@ -33,16 +33,18 @@ void render_separator(struct window *window) {
}
void render_ncontent(struct window *window) {
size_t cursor_y = 0,
i = window->scroll;
while (i < window->content.n_formatted && cursor_y < window->rows) {
render_nline(window->window, cursor_y, window->content.formatted[i]);
if (window->content.formatted[i]->type == ANIM &&
window->content.formatted[i]->anim->is_first_line) {
push_anim_ref_back(window, i);
if (window->content.type == STATIC && window->content.lines != NULL) {
size_t cursor_y = 0,
i = window->scroll;
while (i < window->content.lines->n_formatted && cursor_y < window->rows) {
render_nline(window->window, cursor_y, window->content.lines->formatted[i]);
if (window->content.lines->formatted[i]->type == ANIM &&
window->content.lines->formatted[i]->anim->is_first_line) {
push_anim_ref_back(window, i);
}
i++;
cursor_y++;
}
i++;
cursor_y++;
}
}
......
......@@ -53,34 +53,36 @@ void scroll_separator(struct window *window, int dy) {
}
void scroll_ncontent(struct window *window, int dy) {
if (dy == 1 && window->scroll+window->rows < window->content.n_formatted) {
wscrl(window->window, 1);
window->scroll++;
render_nline(window->window, window->rows-1, window->content.formatted[window->scroll+window->rows-1]);
if (window->content.formatted[window->scroll-1]->type == ANIM &&
(window->content.formatted[window->scroll]->type != ANIM ||
window->content.formatted[window->scroll]->anim->is_first_line)) {
pop_anim_ref_front(window);
}
if (window->content.formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.formatted[window->scroll+window->rows-1]->anim->is_first_line) {
push_anim_ref_back(window, window->scroll+window->rows-1);
}
} else if (dy == -1 && window->scroll > 0) {
wscrl(window->window, -1);
window->scroll--;
render_nline(window->window, 0, window->content.formatted[window->scroll]);
// was there an animation that went out of focus?
if (window->content.formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.formatted[window->scroll+window->rows-1]->anim->is_first_line) {
pop_anim_ref_back(window);
}
// is there a new animation that came into focus?
if (window->content.formatted[window->scroll]->type == ANIM &&
(window->content.formatted[window->scroll+1]->type != ANIM ||
window->content.formatted[window->scroll+1]->anim->is_first_line)) {
push_anim_ref_front(window, window->scroll);
if (window->content.type == STATIC && window->content.lines != NULL) {
if (dy == 1 && window->scroll+window->rows < window->content.lines->n_formatted) {
wscrl(window->window, 1);
window->scroll++;
render_nline(window->window, window->rows-1, window->content.lines->formatted[window->scroll+window->rows-1]);
if (window->content.lines->formatted[window->scroll-1]->type == ANIM &&
(window->content.lines->formatted[window->scroll]->type != ANIM ||
window->content.lines->formatted[window->scroll]->anim->is_first_line)) {
pop_anim_ref_front(window);
}
if (window->content.lines->formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.lines->formatted[window->scroll+window->rows-1]->anim->is_first_line) {
push_anim_ref_back(window, window->scroll+window->rows-1);
}
} else if (dy == -1 && window->scroll > 0) {
wscrl(window->window, -1);
window->scroll--;
render_nline(window->window, 0, window->content.lines->formatted[window->scroll]);
// was there an animation that went out of focus?
if (window->content.lines->formatted[window->scroll+window->rows-1]->type == ANIM &&
window->content.lines->formatted[window->scroll+window->rows-1]->anim->is_first_line) {
pop_anim_ref_back(window);
}
// is there a new animation that came into focus?
if (window->content.lines->formatted[window->scroll]->type == ANIM &&
(window->content.lines->formatted[window->scroll+1]->type != ANIM ||
window->content.lines->formatted[window->scroll+1]->anim->is_first_line)) {
push_anim_ref_front(window, window->scroll);
}
}
wrefresh(window->window);
}
wrefresh(window->window);
}
This diff is collapsed.
/*
* This file is part of telnet-site.
*
* telnet-site is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* telnet-site is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with telnet-site. If not, see <https://www.gnu.org/licenses/>.
*/
#include "splash.h"
#include "data.h"
......@@ -18,11 +35,11 @@ void splash(char *path) {
FILE *fp = fopen(path, "r");
if (fp != NULL) {
splash_window.content.n_raw = read_nlines(fp, &splash_window.content.raw);
splash_window.content.lines->n_raw = read_nlines(fp, &splash_window.content.lines->raw);
fclose(fp);
} else {
fprintf(stderr, "[W] %s:%s:%u: %s: %s\n", binary_name, __FILE__, __LINE__, strerror(errno), path);
splash_window.content.n_raw = gen_err_opening(&splash_window.content.raw);
gen_err_opening(&splash_window.content);
}
splash_window.window = initscr();
......@@ -35,11 +52,12 @@ void splash(char *path) {
splash_window.rows = LINES;
splash_window.scroll = 0;
splash_window.content.anim_refs = NULL;
splash_window.content.n_formatted = flow_nlines(splash_window.content.raw,
splash_window.content.n_raw,
&splash_window.content.formatted,
splash_window.cols);
splash_window.content.lines->anim_refs = NULL;
splash_window.content.lines->n_formatted = flow_nlines(splash_window.content.lines->raw,
splash_window.content.lines->n_raw,
&splash_window.content.lines->formatted,
splash_window.cols,
0);
render_ncontent(&splash_window);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment