jcs
/subtext
/amendments
/66
session: Add custom templating with {{ }}
{{B}} and {{/}} inline are much easier than using printf with ansi()
calls. Also allows some logic in templates like showing different
strings in the main menu depending on the user.
jcs made amendment 66 over 2 years ago
--- session.c Sun Jan 23 13:51:10 2022
+++ session.c Wed Jan 26 13:38:30 2022
@@ -21,6 +21,7 @@
#include "ansi.h"
#include "chat.h"
+#include "mail.h"
#include "subtext.h"
#include "session.h"
#include "user.h"
@@ -29,6 +30,13 @@
#include "console.h"
+#define EXPAND_TO_FIT(var, size, pos, len) { \
+ if (((pos) + (len)) >= (size)) { \
+ (size) += 256; \
+ (var) = xrealloc((var), (size)); \
+ } \
+}
+
struct session **sessions = NULL;
short nsessions = 0;
@@ -70,7 +78,6 @@ session_run(struct uthread *uthread, void *arg)
Handle h;
char date[9];
struct tm *date_tm;
- char *view;
unsigned char c, done = 0;
size_t len;
@@ -82,11 +89,7 @@ session_run(struct uthread *uthread, void *arg)
if (s->node_funcs->setup)
s->node_funcs->setup(s);
- len = session_load_view(s, DB_TEXT_ISSUE_ID, &view);
- if (len) {
- session_output(s, view, len);
- free(view);
- } else {
+ if (!session_output_view(s, DB_TEXT_ISSUE_ID)) {
session_printf(s, "\r\n"
"Welcome to %s (%s)\r\n"
"\r\n", db->config.name, s->node);
@@ -113,18 +116,12 @@ session_run(struct uthread *uthread, void *arg)
sessions_tally++;
session_printf(s, "\r\n"
- "Welcome, %s%s%s, you are the %d%s caller today.\r\n",
- ansi(s, ANSI_BOLD, ANSI_END),
+ "Welcome, {{B}}%s{{/}}, you are the %d%s caller today.\r\n",
s->user ? s->user->username : GUEST_USERNAME,
- ansi(s, ANSI_RESET, ANSI_END),
sessions_tally, ordinal(sessions_tally));
session_flush(s);
- len = session_load_view(s, DB_TEXT_MENU_ID, &view);
- if (len) {
- session_output(s, view, len);
- free(view);
- } else
+ if (!session_output_view(s, DB_TEXT_MENU_ID))
session_output_string(s, "\r\n[ Menu missing! ]\r\n\r\n");
session_flush(s);
@@ -145,6 +142,7 @@ get_another_char:
/* TODO: make letter->command dynamic from a resource */
switch (c) {
case 'c':
+ case 'C':
/* chat */
chat_start(s, NULL);
break;
@@ -155,17 +153,18 @@ get_another_char:
session_flush(s);
done = 1;
break;
+ case 'm':
+ case 'M':
+ mail_menu(s);
+ break;
case 'w':
+ case 'W':
/* who's online */
session_who(s);
break;
case '?':
/* short menu */
- len = session_load_view(s, DB_TEXT_SHORTMENU_ID, &view);
- if (len) {
- session_output(s, view, len);
- free(view);
- } else
+ if (!session_output_view(s, DB_TEXT_SHORTMENU_ID))
session_output_string(s, "\r\n[ Short menu missing! ]\r\n\r\n");
session_flush(s);
break;
@@ -220,20 +219,6 @@ session_close(struct session *session)
free(session);
}
-size_t
-session_printf(struct session *session, const char *format, ...)
-{
- static char session_tbuf[512];
- va_list ap;
- size_t len;
-
- va_start(ap, format);
- len = vsnprintf(session_tbuf, sizeof(session_tbuf), format, ap);
- va_end(ap);
-
- return session_output(session, session_tbuf, len);
-}
-
void
session_flush(struct session *session)
{
@@ -279,10 +264,70 @@ session_output(struct session *session, const char *st
size_t
session_output_string(struct session *session, const char *str)
{
- size_t len = strlen(str);
- return session_output(session, str, len);
+ return session_output(session, str, strlen(str));
}
+size_t
+session_printf(struct session *session, const char *format, ...)
+{
+ static char session_printf_tbuf[256];
+ va_list ap;
+ size_t len;
+ char *expanded;
+
+ len = session_expand_template(session, format, &expanded);
+
+ va_start(ap, format);
+ len = vsnprintf(session_printf_tbuf, sizeof(session_printf_tbuf),
+ expanded, ap);
+ va_end(ap);
+ free(expanded);
+ if (len > sizeof(session_printf_tbuf))
+ panic("session_printf overflow! (%ld > %ld)", len,
+ sizeof(session_printf_tbuf));
+
+ return session_output(session, session_printf_tbuf, len);
+}
+
+size_t
+session_output_view(struct session *session, short id)
+{
+ size_t size;
+ struct bile_object *o;
+ char *view;
+
+ /* can't use bile_read_alloc because we need to null terminate */
+ o = bile_find(db->bile, DB_TEXT_TYPE, id);
+ if (o == NULL)
+ return 0;
+
+ view = xmalloc(o->size + 1);
+ size = bile_read_object(db->bile, o, view, o->size);
+ free(o);
+
+ view[size] = '\0';
+ size = session_output_template(session, view);
+ free(view);
+
+ return size;
+}
+
+size_t
+session_output_template(struct session *session, const char *str)
+{
+ size_t size;
+ char *output;
+
+ size = session_expand_template(session, str, &output);
+ if (!size)
+ return;
+
+ size = session_output(session, output, size);
+ free(output);
+
+ return size;
+}
+
char
session_input_char(struct session *session)
{
@@ -501,13 +546,6 @@ login_bail:
return AUTH_USER_FAILED;
}
-#define EXPAND_TO_FIT(var, size, pos, len) { \
- if (((pos) + (len)) >= (size)) { \
- (size) += 1024; \
- (var) = xrealloc((var), (size)); \
- } \
-}
-
char *
session_bar(struct session *s, char *left_str, char *right_str)
{
@@ -578,94 +616,146 @@ session_bar(struct session *s, char *left_str, char *r
}
size_t
-session_load_view(struct session *session, short id, char **ret)
+session_expand_template(struct session *session, const char *tmpl,
+ char **ret)
{
- char curvar[64];
+ char curvar[128], matchvar[128];
struct tm *now;
- size_t retsize, retpos;
- size_t vsize, vallen;
- short n, j, invar = 0, varlen = 0, valsize, count, pad;
- char *view;
- char c;
-
- vsize = bile_read_alloc(db->bile, DB_TEXT_TYPE, id, &view);
- if (vsize == 0)
- return 0;
-
+ size_t tmpllen, retsize, retpos;
+ size_t vallen;
+ short n, j, quote, invar = 0, varlen = 0, valsize, count, pad, doif,
+ sep;
+ char *varseek, *curvarpos;
+
retsize = 0;
retpos = 0;
*ret = NULL;
+ tmpllen = strlen(tmpl);
- for (n = 0; n < vsize; n++) {
- c = view[n];
-
- if (c == '%' && view[n + 1] == '%') {
- n++;
- if (!invar) {
- invar = 1;
- varlen = 0;
+ for (n = 0; n < tmpllen; n++) {
+ if (invar) {
+ if (!varlen && (tmpl[n] == ' ' || tmpl[n] == '\t'))
continue;
- }
+ if (tmpl[n] == '}' && tmpl[n + 1] == '}') {
+ n++;
+ goto expand_var;
+ } else if (varlen < sizeof(curvar) - 2)
+ curvar[varlen++] = tmpl[n];
+ } else if (tmpl[n] == '{' && tmpl[n + 1] == '{') {
+ invar = 1;
+ varlen = 0;
+ n++;
+ } else if (tmpl[n] == '\r' && tmpl[n + 1] != '\n') {
+ EXPAND_TO_FIT(*ret, retsize, retpos, 2);
+ (*ret)[retpos++] = '\r';
+ (*ret)[retpos++] = '\n';
+ } else {
+ EXPAND_TO_FIT(*ret, retsize, retpos, 1);
+ (*ret)[retpos++] = tmpl[n];
+ }
+
+ continue;
- invar = 0;
- count = 0;
- pad = 0;
- vallen = 0;
- curvar[varlen] = '\0';
+expand_var:
+ invar = 0;
+ count = 0;
+ pad = 0;
+ vallen = 0;
+ curvar[varlen] = '\0';
- if (sscanf(curvar, "%d-%n", &valsize, &count) == 1 &&
- count > 0) {
- /* field of fixed length, either truncated or padded */
- if (valsize > sizeof(curvar))
- valsize = sizeof(curvar);
- /* 32-USERNAME -> USERNAME */
- memmove(curvar, curvar + count, valsize - count);
- pad = 1;
- } else {
+ if (sscanf(curvar, "%[^:]:%d%n", matchvar, &valsize, &count) == 2 &&
+ count > 0) {
+ /* field of fixed length, either truncated or padded */
+ if (valsize > sizeof(curvar))
valsize = sizeof(curvar);
- }
+ varlen = strlcpy(curvar, matchvar, sizeof(curvar));
+ pad = 1;
+ } else {
+ valsize = sizeof(curvar);
+ }
+
+ while (varlen && curvar[varlen - 1] == ' ') {
+ curvar[varlen - 1] = '\0';
+ varlen--;
+ }
+
+ if (strcmp(curvar, "B") == 0) {
+ vallen = strlcpy(curvar,
+ ansi(session, ANSI_BOLD, ANSI_END), valsize);
+ } else if (strcmp(curvar, "/") == 0) {
+ vallen = strlcpy(curvar,
+ ansi(session, ANSI_RESET, ANSI_END), valsize);
+ } else if (strcmp(curvar, "node") == 0) {
+ vallen = strlcpy(curvar, session->node, valsize);
+ } else if (strcmp(curvar, "phone_number") == 0) {
+ vallen = strlcpy(curvar, db->config.phone_number, valsize);
+ } else if (strcmp(curvar, "time") == 0) {
+ now = localtime((time_t *)&Time);
+ vallen = strftime(curvar, valsize, "%H:%M", now);
+ } else if (strcmp(curvar, "username") == 0) {
+ vallen = strlcpy(curvar, session->user ?
+ session->user->username : GUEST_USERNAME, valsize);
+ } else if (strchr(curvar, '?') != NULL && strchr(curvar, ':') != NULL) {
+ /* user ? " Settings" : "Signup for Account" */
+ varseek = curvar;
+ while (varseek[0] == ' ')
+ varseek++;
+ sep = strpos_quoted(varseek, '?');
+ if (sep == -1)
+ continue;
+ memcpy(matchvar, varseek, sep);
+ matchvar[sep] = '\0';
+ curvarpos = varseek + sep + 1;
+ while (matchvar[sep - 1] == ' ')
+ matchvar[--sep] = '\0';
- if (strcmp(curvar, "NODE") == 0) {
- vallen = strlcpy(curvar, session->node, valsize);
- } else if (strcmp(curvar, "PHONE_NUMBER") == 0) {
- vallen = strlcpy(curvar, db->config.phone_number, valsize);
- } else if (strcmp(curvar, "TIME") == 0) {
- now = localtime((time_t *)&Time);
- vallen = strftime(curvar, valsize, "%H:%M", now);
- } else if (strcmp(curvar, "USERNAME") == 0) {
- vallen = strlcpy(curvar, session->user ?
- session->user->username : GUEST_USERNAME,
- valsize);
- } else if (strcmp(curvar, "ANSI_BOLD") == 0) {
- vallen = strlcpy(curvar,
- ansi(session, ANSI_BOLD, ANSI_END), valsize);
- } else if (strcmp(curvar, "ANSI_RESET") == 0) {
- vallen = strlcpy(curvar,
- ansi(session, ANSI_RESET, ANSI_END), valsize);
- }
+ if (strcmp(matchvar, "user") == 0)
+ doif = (session->user != NULL);
+ else if (strcmp(matchvar, "sysop") == 0)
+ doif = (session->user && session->user->is_sysop);
+ else
+ doif = 0;
- if (vallen) {
- if (pad && vallen < valsize) {
- while (vallen < valsize)
- curvar[vallen++] = ' ';
- curvar[vallen] = '\0';
+ sep = strpos_quoted(curvarpos, ':');
+ if (sep == -1)
+ continue;
+
+ if (doif) {
+ while (curvarpos[0] == ' ') {
+ curvarpos++;
+ sep--;
}
- EXPAND_TO_FIT(*ret, retsize, retpos, vallen);
- memcpy(*ret + retpos, curvar, vallen);
- retpos += vallen;
+ memcpy(matchvar, curvarpos, sep);
+ matchvar[sep] = '\0';
+ } else {
+ while (curvarpos[sep] == ' ') {
+ curvarpos++;
+ sep--;
+ }
+ strlcpy(matchvar, curvarpos + sep, sizeof(matchvar));
}
- } else if (invar) {
- if (varlen < sizeof(curvar) - 2)
- curvar[varlen++] = c;
- } else if (c == '\r' && view[n + 1] != '\n') {
- EXPAND_TO_FIT(*ret, retsize, retpos, 2);
- (*ret)[retpos++] = '\r';
- (*ret)[retpos++] = '\n';
- } else {
- EXPAND_TO_FIT(*ret, retsize, retpos, 1);
- (*ret)[retpos++] = c;
+ sep = strlen(matchvar);
+ while (matchvar[sep - 1] == ' ')
+ matchvar[--sep] = '\0';
+
+ /* remove leading and trailing quotes */
+ vallen = sep - 2;
+ memcpy(curvar, matchvar + 1, vallen);
+ curvar[vallen] = '\0';
}
+
+ if (vallen) {
+ if (pad && vallen < valsize) {
+ while (vallen < valsize)
+ curvar[vallen++] = ' ';
+ curvar[vallen] = '\0';
+ }
+ EXPAND_TO_FIT(*ret, retsize, retpos, vallen);
+ memcpy(*ret + retpos, curvar, vallen);
+ retpos += vallen;
+ }
}
+ (*ret)[retpos] = '\0';
return retpos;
}
@@ -675,9 +765,7 @@ session_pause_return(struct session *s, short enforce,
{
unsigned char c;
- session_printf(s, "%sPress %s<Enter>%s ",
- ansi(s, ANSI_RESET, ANSI_END), ansi(s, ANSI_BOLD, ANSI_END),
- ansi(s, ANSI_RESET, ANSI_END));
+ session_output_template(s, "{{/}}Press {{B}}<Enter>{{/}} ");
if (msg)
session_output_string(s, msg);
else
@@ -702,13 +790,9 @@ session_who(struct session *s)
unsigned long idle;
short n;
- session_printf(s,
- "%sWho's Online%s\r\n",
- ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END));
-
- session_printf(s,
- "%sNode User Via Speed Idle%s\r\n",
- ansi(s, ANSI_BOLD, ANSI_END), ansi(s, ANSI_RESET, ANSI_END));
+ session_output_template(s, "{{B}}Who's Online{{/}}\r\n");
+ session_output_template(s,
+ "{{B}}Node User Via Speed Idle{{/}}\r\n");
session_flush(s);
for (n = 0; n < nsessions; n++) {
--- session.h Mon Jan 17 21:01:23 2022
+++ session.h Mon Jan 24 11:07:42 2022
@@ -89,13 +89,16 @@ void session_close(struct session *session);
void session_idle(struct session *session);
char session_input_char(struct session *session);
void session_flush(struct session *session);
-size_t session_printf(struct session *session, const char *format, ...);
size_t session_output(struct session *session, const char *str, size_t len);
size_t session_output_string(struct session *session, const char *str);
+size_t session_printf(struct session *session, const char *format, ...);
+size_t session_output_view(struct session *session, short id);
+size_t session_output_template(struct session *session, const char *str);
+size_t session_expand_template(struct session *session, const char *str,
+ char **ret);
char *session_field_input(struct session *session, unsigned short size,
unsigned short width, char mask);
char *session_bar(struct session *s, char *left_str, char *right_str);
-size_t session_load_view(struct session *session, short id, char **ret);
void session_pause_return(struct session *s, short enforce, char *msg);
void session_who(struct session *s);
--- settings.c Sat Jan 22 20:35:04 2022
+++ settings.c Mon Jan 24 10:32:19 2022
@@ -286,7 +286,7 @@ view_editor_key_down(struct focusable *focusable, Even
if ((event->modifiers & cmdKey) != 0) {
switch (k) {
case 'c':
- TECut(view_editor->te);
+ TECopy(view_editor->te);
break;
case 'v':
TEPaste(view_editor->te);
--- util.c Sat Jan 22 20:44:46 2022
+++ util.c Wed Jan 26 10:58:59 2022
@@ -200,11 +200,39 @@ ordinal(unsigned short n)
}
}
-static char ostype_s[5];
+long
+strpos_quoted(char *str, char c)
+{
+ long pos;
+ unsigned char quot = 0;
+
+ for (pos = 0; str[pos] != '\0'; pos++) {
+ if (quot) {
+ if (str[pos] == '\\') {
+ pos++;
+ continue;
+ }
+ if (str[pos] == quot)
+ quot = 0;
+ continue;
+ } else {
+ if (str[pos] == '"') {
+ quot = str[pos];
+ continue;
+ }
+ if (str[pos] == c)
+ return pos;
+ }
+ }
+
+ return -1;
+}
char *
OSTypeToString(OSType type)
{
+ static char ostype_s[5];
+
ostype_s[0] = (unsigned char)((type >> 24) & 0xff);
ostype_s[1] = (unsigned char)((type >> 16) & 0xff);
ostype_s[2] = (unsigned char)((type >> 8) & 0xff);
--- util.h Fri Jan 21 14:28:14 2022
+++ util.h Wed Jan 26 10:59:17 2022
@@ -72,6 +72,7 @@ short getline(char *str, size_t len, char **ret);
size_t strlcpy(char *dst, const char *src, size_t dsize);
size_t strlcat(char *dst, const char *src, size_t dsize);
const char *ordinal(unsigned short n);
+long strpos_quoted(char *str, char c);
char *OSTypeToString(OSType type);
unsigned long xorshift32(void);