Compare commits

...

5 Commits

Author SHA1 Message Date
CismonX 0956b2cd92
feat: add rl-loop utility program
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-04-10 22:52:14 +08:00
CismonX ade349d1e8
chore: allow arify to be built without readline
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-04-07 05:06:04 +08:00
CismonX 709bc78b5f
chore: switch to woodpecker ci
ci/woodpecker/push/woodpecker Pipeline was successful Details
2024-03-31 21:22:18 +08:00
CismonX 93a6850de7
chore: clean up build scripts
* remove checks that we do not use
* autoconf-archive is now mandatory
2024-03-31 20:12:34 +08:00
CismonX 5526a0bc77
refactor: cleaner way to hard-code libdir 2023-12-25 17:36:25 +08:00
10 changed files with 299 additions and 58 deletions

View File

@ -6,19 +6,21 @@
# this notice are preserved. This file is offered as-is, without any warranty.
#
kind: pipeline
type: docker
name: default
# For history build logs, see <https://ci.cismon.net/repos/cismonx/arif>.
steps:
- name: build
image: debian:bookworm-slim
when:
- event: [push, tag, manual]
branch: master
commands:
- apt-get -y update && apt-get -y install
gcc g++ make pkgconf autoconf automake libtool
autoconf-archive dejagnu texinfo libreadline-dev librime-dev
- mkdir build && cd build && autoreconf -i ..
- ../configure --with-readline --with-rime --enable-arif-debug
- ../configure --with-readline --with-rime
--enable-arif-debug --enable-arify --enable-rl-loop
CFLAGS='-O0 -g -std=c99 -Wall -Wextra -Wpedantic -Wshadow'
CPPFLAGS='-D_POSIX_C_SOURCE=200112L'
- make

View File

@ -19,15 +19,11 @@ LT_INIT([dlopen])
# -- Checks for programs --
AC_PROG_AWK
AC_PROG_CC
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
AC_PROG_MKDIR_P
AC_PROG_SED
# -- Checks for libraries --
@ -57,38 +53,25 @@ ARIF_ARG_ENABLE([arif-debug], [no], [debugging features for ARIF], [], [
])
AM_CONDITIONAL([ENABLE_DEBUG], [test x${enable_debug} = xyes])
ARIF_ARG_ENABLE([arify], [yes], [the arify command line tool], [], [
ARIF_ARG_ENABLE([arify], [yes], [the arify program])
AM_CONDITIONAL([BUILD_ARIFY], [test x${enable_arify} != xno])
ARIF_ARG_ENABLE([rl-loop], [no], [the rl-loop program], [], [
AS_VAR_IF([with_readline], [no], [
AC_MSG_WARN(m4_normalize([
The arify command line tool will not be built,
as the ARIF library is configured without GNU Readline.
AC_MSG_ERROR(m4_normalize([
The rl-loop program could not be built,
since mandatory dependency GNU Readline is not configured.
]))
AS_VAR_SET([enable_arify], [no])
])
])
AM_CONDITIONAL([BUILD_ARIFY], [test x${enable_arify} != xno])
AM_CONDITIONAL([BUILD_RL_LOOP], [test x${enable_rl_loop} != xno])
# -- Checks for compiler builtins and attributes --
ARIF_TRY_APPLY([AX_GCC_BUILTIN], [__builtin_unreachable])
ARIF_TRY_APPLY([AX_GCC_FUNC_ATTRIBUTE], [constructor], [destructor])
ARIF_TRY_APPLY([AX_GCC_VAR_ATTRIBUTE], [unused])
# -- Checks for header files --
AC_CHECK_HEADERS([fcntl.h strings.h unistd.h])
# -- Checks for typedefs, structures, and compiler characteristics --
AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_SIZE_T
# -- Checks for library functions
AC_FUNC_MALLOC
AC_FUNC_REALLOC
AC_CHECK_FUNCS([atexit memchr strcasecmp])
AX_GCC_BUILTIN([__builtin_unreachable])
AX_GCC_FUNC_ATTRIBUTE([constructor])
AX_GCC_FUNC_ATTRIBUTE([destructor])
AX_GCC_VAR_ATTRIBUTE([unused])
# -- Output --

View File

@ -6,9 +6,11 @@
# this notice are preserved. This file is offered as-is, without any warranty.
#
bin_PROGRAMS =
include_HEADERS =
noinst_HEADERS =
pkglib_LTLIBRARIES =
dist_man1_MANS =
dist_man3_MANS =
@ -25,3 +27,14 @@ if BUILD_ARIF_RIME
rime_la_SOURCES = arif_rime.c arif_rime_workaround.cc
endif # BUILD_ARIF_RIME
if BUILD_RL_LOOP
bin_PROGRAMS += rl-loop
dist_man1_MANS += rl-loop.1
rl_loop_CPPFLAGS = $(READLINE_CFLAGS)
rl_loop_LDADD = $(READLINE_LIBS)
rl_loop_SOURCES = rl_loop.c
endif # BUILD_RL_LOOP

88
examples/rl-loop.1 Normal file
View File

@ -0,0 +1,88 @@
.TH RL-LOOP 1 "Apr 10, 2024" 0.1.0 "ARIF User Manual"
.
.SH NAME
rl-loop - Readline loop
.
.SH SYNOPSIS
.B rl-loop
.RI [ options ]
.I pathname
.RI [ args ]
.
.SH DESCRIPTION
Interactively reads user input with GNU Readline.
.PP
For each line of user input, executes the program referred to by
.IR pathname ,
passing
.I args
as its command-line arguments.
.
.SH OPTIONS
.TP
.B -e
Executes
.I pathname
even if the input line is empty.
.TP
\fB\-i\fR \fIreplace-idx\fR
When executing
.IR pathname ,
replaces the value of the nth (starting from 1) argument referred to by
.I replace-idx
with the line of text.
.IP
If this option is not provided, or has a value of 0, the line will be written
to standard input instead.
.TP
\fB\-n\fR \fIname\fR
Readline application name.
Default value is "rl-loop".
.IP
Allows application-specific settings in a Readline init file.
See "Conditional Init Constructs" subsection of GNU Readline's user manual.
.TP
\fB\-p\fR \fIprompt\fR
The prompt text to be printed before reading user input.
Default value is "% ".
.
.SH EXIT STATUS
The program exits with status 0 if there are no errors, or 1 if otherwise.
.
.SH NOTES
The functionalities provided by this program can be achived with a simple
Bash script
.RB ( "read -e"
and friends).
.PP
However, Bash resets Readline state after each
.BR read (1),
necessitating the need to reinstall the completion functions if they are
not handled by Bash, which could be tricky to workaround.
.PP
You may find this program useful when dealing with such cases (e.g. when using
.BR arify (1)).
.
.SH EXAMPLES
Copy each line to the Wayland clipboard:
.PP
.in +4n
.EX
$ rl-loop -i4 -- wl-copy -t text/plain -- %
.EE
.in
.
.SH COPYRIGHT
Copyright (C) 2024 CismonX <admin@cismon.net>
.PP
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
.PP
You should have received a copy of the license along with this document.
If not, see <https://www.gnu.org/licenses/fdl-1.3.html>.
.
.SH SEE ALSO
.BR bash (1),
.BR readline (3)

171
examples/rl_loop.c Normal file
View File

@ -0,0 +1,171 @@
/**
* arif/examples/rl_loop.c
* ----
*
* Copyright (C) 2024 CismonX <admin@cismon.net>
*
* This file is part of ARIF, Another Readline Input Framework.
*
* ARIF 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.
*
* ARIF 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 ARIF. If not, see <https://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_READLINE
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <readline/history.h>
#include <readline/readline.h>
// Forward declaration start
static void print_usage (char const *);
static int send_line (char *, int, char *[]);
// Forward declaration end
static void
print_usage (
char const *program
) {
fprintf(stderr, "Usage: %s [options] pathname [args]\n\n", program);
fputs("See the rl-loop(1) man page for details.\n", stderr);
}
static int
send_line (
char *line,
int replace_idx,
char *argv[]
) {
int pfds[2];
if (0 != pipe(pfds)) {
fprintf(stderr, "pipe(): %s\n", strerror(errno));
return -1;
}
pid_t child = fork();
if (child < 0) {
fprintf(stderr, "fork(): %s\n", strerror(errno));
return -1;
}
if (child != 0) {
close(pfds[0]);
if (replace_idx == 0) {
for (size_t line_len = rl_end; line_len > 0; ) {
ssize_t nbytes = write(pfds[1], line, line_len);
if (nbytes < 0) {
fprintf(stderr, "write(): %s\n", strerror(errno));
break;
}
line_len -= nbytes;
}
}
close(pfds[1]);
if (-1 == waitpid(child, NULL, 0)) {
fprintf(stderr, "waitpid(): %s\n", strerror(errno));
return -1;
}
} else {
close(pfds[1]);
if (-1 == dup2(pfds[0], STDIN_FILENO)) {
fprintf(stderr, "dup2(): %s\n", strerror(errno));
return -1;
}
close(pfds[0]);
if (replace_idx != 0) {
argv[replace_idx] = line;
}
if (0 != execvp(argv[0], argv)) {
fprintf(stderr, "execvp(): %s: %s\n", argv[0], strerror(errno));
return -1;
}
}
return 0;
}
int
main (
int argc,
char *argv[]
) {
char const *name = "rl-loop";
char const *prompt = "% ";
int replace_idx = 0;
bool send_empty = false;
for (int opt; -1 != (opt = getopt(argc, argv, "ei:n:p:")); ) {
switch (opt) {
case 'e':
send_empty = true;
break;
case 'i':
replace_idx = atoi(optarg);
break;
case 'n':
name = optarg;
break;
case 'p':
prompt = optarg;
break;
case '?':
default:
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
}
argc -= optind;
if (argc == 0) {
print_usage(argv[0]);
exit(EXIT_FAILURE);
}
if (replace_idx < 0 || replace_idx >= argc) {
fprintf(stderr, "%s: bad option -i, expected [0, %d), got %d\n",
argv[0], argc, replace_idx);
exit(EXIT_FAILURE);
}
argv += optind;
rl_readline_name = name;
for (char *line; ; free(line)) {
line = readline(prompt);
if (line == NULL) {
break;
}
if (rl_end > 0) {
add_history(line);
} else {
if (!send_empty) {
continue;
}
}
if (0 != send_line(line, replace_idx, argv)) {
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
}
#endif // defined(HAVE_READLINE)

View File

@ -48,5 +48,5 @@
#endif
#ifndef ARIF_LIBDIR
# define ARIF_LIBDIR /usr/local/lib
# define ARIF_LIBDIR "/usr/local/lib"
#endif

View File

@ -33,7 +33,6 @@ AC_DEFUN([ARIF_ARG_ENABLE], [
m4_popdef([enable_var_])
])
dnl
dnl ARIF_CHECK_PKG(package, version, package-name, [action-if-not-given],
dnl [action-if-with], [action-if-without])
@ -58,15 +57,3 @@ AC_DEFUN([ARIF_CHECK_PKG], [
])
m4_popdef([with_var_])
])
dnl
dnl ARIF_TRY_APPLY(macro-name, ...)
dnl
dnl If `macro-name' is defined, for each remaining argument,
dnl applies the macro to that argument.
dnl
AC_DEFUN([ARIF_TRY_APPLY], [
m4_ifdef([$1], [
m4_foreach([val_], [m4_shift($@)], [$1(val_)])
])
])

View File

@ -9,7 +9,7 @@
bin_PROGRAMS =
lib_LTLIBRARIES = libarif.la
ARIF_CPPFLAGS_ = -I$(top_srcdir)/include -DARIF_LIBDIR=$(libdir)
ARIF_CPPFLAGS_ = -I$(top_srcdir)/include -DARIF_LIBDIR="\"$(libdir)\""
libarif_la_CPPFLAGS = $(READLINE_CFLAGS) $(ARIF_CPPFLAGS_)
libarif_la_LIBADD = $(READLINE_LIBS)

View File

@ -59,10 +59,9 @@
# error "__attribute__((destructor)) not supported"
#endif
#define ARIFY_ENGINE_PATH_(libdir, engine) \
#libdir "/arif/" engine ARIF_SHLIB_SUFFIX
#define ARIFY_ENGINE_PATH(libdir, engine) ARIFY_ENGINE_PATH_(libdir, engine)
#define ARIFY_ENGINE_SYM(engine) "arif_" engine "_engine"
#define ARIFY_ENGINE_LIB(engine) \
ARIF_LIBDIR "/arif/" engine ARIF_SHLIB_SUFFIX
#define ARIFY_ENGINE_SYM(engine) "arif_" engine "_engine"
#ifndef ARIFY_MAX_PAGE_SIZE
# define ARIFY_MAX_PAGE_SIZE 99
@ -313,11 +312,10 @@ load_engine (
char *var_tmp = NULL;
if (var_name == NULL) {
size_t lib_name_len
= sizeof ARIFY_ENGINE_PATH(ARIF_LIBDIR, "") + strlen(lib_name);
size_t lib_name_len = sizeof ARIFY_ENGINE_LIB("") + strlen(lib_name);
lib_tmp = malloc(sizeof(char) * lib_name_len);
assert(lib_tmp != NULL);
sprintf(lib_tmp, ARIFY_ENGINE_PATH(ARIF_LIBDIR, "%s"), lib_name);
sprintf(lib_tmp, ARIFY_ENGINE_LIB("%s"), lib_name);
size_t var_name_len = sizeof ARIFY_ENGINE_SYM("") + strlen(lib_name);
var_tmp = malloc(sizeof(char) * var_name_len);

View File

@ -35,8 +35,7 @@
#include "arif.h"
#include "arif_defs.h"
#define ARIFY_PRELOAD_LIB_(libdir) #libdir "/libarify" ARIF_SHLIB_SUFFIX
#define ARIFY_PRELOAD_LIB(libdir) ARIFY_PRELOAD_LIB_(libdir)
#define ARIFY_PRELOAD_LIB ARIF_LIBDIR "/libarify" ARIF_SHLIB_SUFFIX
struct options {
char *frontend;
@ -172,7 +171,7 @@ main (
assert(program != NULL);
struct options opts = {
.preload = ARIFY_PRELOAD_LIB(ARIF_LIBDIR),
.preload = ARIFY_PRELOAD_LIB,
};
argv += parse_options(argc, argv, &opts);
set_envs(&opts);