Compare commits

..

1 Commits

Author SHA1 Message Date
CismonX e2a85db4a7 feat: add completion scripts (WIP) 2023-12-05 03:52:57 +08:00
14 changed files with 137 additions and 300 deletions

View File

@ -6,21 +6,19 @@
# this notice are preserved. This file is offered as-is, without any warranty.
#
# For history build logs, see <https://ci.cismon.net/repos/cismonx/arif>.
kind: pipeline
type: docker
name: default
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 --enable-arify --enable-rl-loop
- ../configure --with-readline --with-rime --enable-arif-debug
CFLAGS='-O0 -g -std=c99 -Wall -Wextra -Wpedantic -Wshadow'
CPPFLAGS='-D_POSIX_C_SOURCE=200112L'
- make

View File

@ -8,7 +8,7 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = doc examples include src tests
SUBDIRS = completions doc examples include src tests
pkgconfig_DATA = arif.pc

23
completions/Makefile.am Normal file
View File

@ -0,0 +1,23 @@
#
# Copyright (C) 2023 CismonX <admin@cismon.net>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty, provided the copyright notice and
# this notice are preserved. This file is offered as-is, without any warranty.
#
dist_bashcompletion_DATA =
dist_zshcompletion_DATA =
if INSTALL_BASH_COMPLETION
dist_bashcompletion_DATA += bash/arify
endif # INSTALL_BASH_COMPLETION
if INSTALL_ZSH_COMPLETION
dist_zshcompletion_DATA += zsh/_arify
endif # INSTALL_ZSH_COMPLETION

5
completions/bash/arify Normal file
View File

@ -0,0 +1,5 @@
_arify()
{
}
complete -F _arify arify

1
completions/zsh/_arify Normal file
View File

@ -0,0 +1 @@
#compdef arify

View File

@ -19,11 +19,15 @@ 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 --
@ -53,31 +57,61 @@ 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 program])
AM_CONDITIONAL([BUILD_ARIFY], [test x${enable_arify} != xno])
ARIF_ARG_ENABLE([rl-loop], [no], [the rl-loop program], [], [
ARIF_ARG_ENABLE([arify], [yes], [the arify command line tool], [], [
AS_VAR_IF([with_readline], [no], [
AC_MSG_ERROR(m4_normalize([
The rl-loop program could not be built,
since mandatory dependency GNU Readline is not configured.
AC_MSG_WARN(m4_normalize([
The arify command line tool will not be built,
as the ARIF library is configured without GNU Readline.
]))
AS_VAR_SET([enable_arify], [no])
])
])
AM_CONDITIONAL([BUILD_RL_LOOP], [test x${enable_rl_loop} != xno])
AM_CONDITIONAL([BUILD_ARIFY], [test x${enable_arify} != xno])
ARIF_ARG_WITHDIR([bashcompletion],
['$(datadir)/bash-completion/completions'], [bash completion],
[
AS_VAR_SET([with_bashcompletiondir], [no])
])
AM_CONDITIONAL([INSTALL_BASH_COMPLETION],
[test x${with_bashcompletiondir} != xno])
ARIF_ARG_WITHDIR([zshcompletion],
['$(datadir)/zsh/site-functions'], [zsh completion],
[
AS_VAR_SET([with_zshcompletiondir], [no])
])
AM_CONDITIONAL([INSTALL_ZSH_COMPLETION],
[test x${with_zshcompletiondir} != xno])
# -- Checks for compiler builtins and attributes --
AX_GCC_BUILTIN([__builtin_unreachable])
AX_GCC_FUNC_ATTRIBUTE([constructor])
AX_GCC_FUNC_ATTRIBUTE([destructor])
AX_GCC_VAR_ATTRIBUTE([unused])
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])
# -- Output --
PKG_INSTALLDIR()
AC_CONFIG_FILES([
Makefile
completions/Makefile
doc/Makefile
examples/Makefile
include/Makefile

View File

@ -6,11 +6,9 @@
# 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 =
@ -27,14 +25,3 @@ 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

View File

@ -1,88 +0,0 @@
.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)

View File

@ -1,171 +0,0 @@
/**
* 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,6 +33,39 @@ AC_DEFUN([ARIF_ARG_ENABLE], [
m4_popdef([enable_var_])
])
dnl
dnl ARIF_ARG_WITHDIR(name, default-value, description, [action-if-not-given],
dnl [action-if-with], [action-if-without])
dnl
dnl Provides an option to specify a custom non-standard directory where some
dnl objects can be installed to.
dnl
AC_DEFUN([ARIF_ARG_WITHDIR], [
m4_pushdef([withdir_var_], [with_]m4_translit([$1], [-+.], [___])[dir])
AC_MSG_CHECKING(m4_normalize([for $3 directory]))
AC_ARG_WITH([$1dir], m4_normalize([
AS_HELP_STRING([--with-$1dir[[=$2]]], [$3 directory])
]), [
AS_VAR_IF(withdir_var_, [yes], [
AS_VAR_SET(withdir_var_, [$2])
])
], [
$4
AS_VAR_IF(withdir_var_, [yes], [
AS_VAR_SET(withdir_var_, [$2])
])
])
AS_VAR_IF(withdir_var_, [no], [
AC_MSG_RESULT([no])
$5
], [
AC_SUBST([$1dir], "${withdir_var_}")
AC_MSG_RESULT(${withdir_var_})
$6
])
m4_popdef([withdir_var_])
])
dnl
dnl ARIF_CHECK_PKG(package, version, package-name, [action-if-not-given],
dnl [action-if-with], [action-if-without])
@ -57,3 +90,15 @@ 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,9 +59,10 @@
# error "__attribute__((destructor)) not supported"
#endif
#define ARIFY_ENGINE_LIB(engine) \
ARIF_LIBDIR "/arif/" engine ARIF_SHLIB_SUFFIX
#define ARIFY_ENGINE_SYM(engine) "arif_" engine "_engine"
#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"
#ifndef ARIFY_MAX_PAGE_SIZE
# define ARIFY_MAX_PAGE_SIZE 99
@ -312,10 +313,11 @@ load_engine (
char *var_tmp = NULL;
if (var_name == NULL) {
size_t lib_name_len = sizeof ARIFY_ENGINE_LIB("") + strlen(lib_name);
size_t lib_name_len
= sizeof ARIFY_ENGINE_PATH(ARIF_LIBDIR, "") + strlen(lib_name);
lib_tmp = malloc(sizeof(char) * lib_name_len);
assert(lib_tmp != NULL);
sprintf(lib_tmp, ARIFY_ENGINE_LIB("%s"), lib_name);
sprintf(lib_tmp, ARIFY_ENGINE_PATH(ARIF_LIBDIR, "%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,7 +35,8 @@
#include "arif.h"
#include "arif_defs.h"
#define ARIFY_PRELOAD_LIB ARIF_LIBDIR "/libarify" ARIF_SHLIB_SUFFIX
#define ARIFY_PRELOAD_LIB_(libdir) #libdir "/libarify" ARIF_SHLIB_SUFFIX
#define ARIFY_PRELOAD_LIB(libdir) ARIFY_PRELOAD_LIB_(libdir)
struct options {
char *frontend;
@ -171,7 +172,7 @@ main (
assert(program != NULL);
struct options opts = {
.preload = ARIFY_PRELOAD_LIB,
.preload = ARIFY_PRELOAD_LIB(ARIF_LIBDIR),
};
argv += parse_options(argc, argv, &opts);
set_envs(&opts);