sob

Simple output bar
git clone https://git.sinitax.com/codemadness/sob
Log | Files | Refs | README | LICENSE | Upstream | sfeed.txt

commit 99d263504572720db6a3a78f1d1b7e0b50060ca7
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date:   Wed,  1 Oct 2014 22:45:24 +0000

initial version

Diffstat:
A.gitignore | 3+++
ALICENSE | 21+++++++++++++++++++++
AMakefile | 48++++++++++++++++++++++++++++++++++++++++++++++++
AREADME | 25+++++++++++++++++++++++++
ATODO | 20++++++++++++++++++++
Aarg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aconfig.def.h | 43+++++++++++++++++++++++++++++++++++++++++++
Aconfig.mk | 25+++++++++++++++++++++++++
Ascripts/complete_word | 21+++++++++++++++++++++
Ascripts/history | 18++++++++++++++++++
Asob.1 | 33+++++++++++++++++++++++++++++++++
Asob.c | 593+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astrlcpy.c | 47+++++++++++++++++++++++++++++++++++++++++++++++
Autil.h | 5+++++
14 files changed, 965 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +sob +*.o +core diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +(c) 2014 Hiltjo Posthuma <hiltjo@codemadness.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,48 @@ +include config.mk + +SRC = sob.c strlcpy.c +HDR = arg.h config.def.h util.h +OBJ = ${SRC:.c=.o} + +all: options sob + +options: + @echo sob build options: + @echo "CFLAGS = ${CFLAGS}" + @echo "LDFLAGS = ${LDFLAGS}" + @echo "CC = ${CC}" + +.c.o: + @echo CC $< + @${CC} -c ${CFLAGS} $< + +${OBJ}: config.mk config.h + +config.h: + @echo creating $@ from config.def.h + @cp config.def.h $@ + +sob: ${OBJ} + @echo CC -o $@ + @${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + @echo cleaning + @rm -f sob ${OBJ} + +install: all + @echo installing executable file to ${DESTDIR}${PREFIX}/bin + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f sob ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/sob + @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 + @mkdir -p ${DESTDIR}${MANPREFIX}/man1 + @sed "s/VERSION/${VERSION}/g" < sob.1 > ${DESTDIR}${MANPREFIX}/man1/sob.1 + +uninstall: + @echo removing executable file from ${DESTDIR}${PREFIX}/bin + @rm -f ${DESTDIR}${PREFIX}/bin/sob + @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/sob.1 + +.PHONY: all options clean dist install uninstall diff --git a/README b/README @@ -0,0 +1,25 @@ +Simple output bar +================= + + +Dependencies +------------ +- Ncurses (at the moment, alternatives will be investigated). +- libc + + +Features +-------- +- Custom prompt. +- Easy to write scripts to pipe input/output. +- Custom action on SIGWINCH (window resize). +- Familiar and customizable keybinds (in config.h). +- Example scripts for: + - Word completion. + - History + - Yank line (xsel). + + +License +------- +See LICENSE file. diff --git a/TODO b/TODO @@ -0,0 +1,20 @@ +13:32 <@TLH> Evil_Bob: please support C-p for up in history C-n for down in history C-f for forward cursor C-b for backwards, C-a and + C-e, C-u, C-k (kill to rest of line), C-y, C-w + 13:32 <@TLH> what else + 13:32 <@TLH> C-j + 13:32 <@TLH> :P + 13:33 <@TLH> C-m as an alias to C-j + +- test draw on wrapped lines. + +- line_yank doesn't work with xclip, but works with xsel... + +- optimize: + reduce redraws (line_redraw and line_cursor_update). + +- selections / marks? (delete/pipe etc on selection)? +- cycle completions? (with tab for example). +- keybind to go to word next and previous (ctrl+arrow left/right). +- prompt callback? allow to update prompt? + +- try libedit, else just use ncurses. diff --git a/arg.h b/arg.h @@ -0,0 +1,63 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/config.def.h b/config.def.h @@ -0,0 +1,43 @@ +static const char *prompt = "> "; +static char id[256] = ""; +static const char *completewordcmd[] = { "./scripts/complete_word", id, NULL }; +static const char *historycmd[] = { "./scripts/history", id, NULL }; +static const char *yankcmd[] = { "/bin/sh", "-c", "/bin/xsel -i -p", NULL }; +static const char *resizecmd = "tmux resize-pane -y 4 2> /dev/null"; + +static struct keybind { + int key; + void (*func)(void); +} keybinds[] = { + { CONTROL('T'), line_delwordcursor }, + { CONTROL('A'), line_cursor_begin }, + { CONTROL('E'), line_cursor_end }, + { KEY_HOME, line_cursor_begin }, + { KEY_END, line_cursor_end }, + { CONTROL('B'), line_cursor_prev }, + { KEY_LEFT, line_cursor_prev }, + { CONTROL('F'), line_cursor_next }, + { KEY_RIGHT, line_cursor_next }, + { CONTROL('W'), line_delwordback }, + { CONTROL('H'), line_delcharback }, + { CONTROL('U'), line_clear }, + { KEY_DL, line_clear }, + { CONTROL('K'), line_deltoend }, + { KEY_SDC, line_delcharnext }, + { KEY_DC, line_delcharnext }, + { KEY_BACKSPACE, line_delcharback }, + { CONTROL('M'), line_newline }, + { CONTROL('J'), line_newline }, + { '\r', line_newline }, + { '\n', line_newline }, + { KEY_ENTER, line_newline }, + { CONTROL('Y'), line_yank }, + { KEY_EXIT, line_exit }, + { 0x04, line_exit }, /* EOT */ + { KEY_EOL, line_deltoend }, + { KEY_UP, history_menu }, + { KEY_DOWN, history_menu }, + { CONTROL('P'), history_menu }, + { CONTROL('N'), history_menu }, + { '\t', complete_word }, +}; diff --git a/config.mk b/config.mk @@ -0,0 +1,25 @@ +VERSION = 0.1 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +# includes and libs +INCS = -I. -I/usr/include +LIBS = -L/usr/lib -lc -lncurses + +# flags +CPPFLAGS = -DVERSION=\"${VERSION}\" -D_POSIX_C_SOURCE=200809 -D_BSD_SOURCE + +# debug +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +#LDFLAGS = ${LIBS} + +# release +CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} +LDFLAGS = -s ${LIBS} + +# compiler and linker +CC = cc diff --git a/scripts/complete_word b/scripts/complete_word @@ -0,0 +1,21 @@ +#!/bin/sh + +grepword() { + grep -E "^$1" < out.log +} + +id="" +test x"$1" != x"" && id="$1" +read -r word + +#if test x"$DISPLAY" = x""; then +# line=$(grepword "$word" | sort | uniq | slmenu -l 20) +#else +# line=$(grepword "$word" | sort | uniq | dmenu -l 20) +#fi + +line=$(grepword "$word" | sort | uniq | dmenu -l 20) + +if test x"$line" != x""; then + printf '%s\n' "$line" +fi diff --git a/scripts/history b/scripts/history @@ -0,0 +1,18 @@ +#!/bin/sh +id="" +test x"$1" != x"" && id="$1" +file="out.log" + +test -f "$file" || exit 1 + +#if test x"$DISPLAY" = x""; then +# line=$(tail -n 100 "$file" | slmenu -l 20) +#else +# line=$(tail -n 100 "$file" | dmenu -l 20) +#fi + +line=$(tail -n 100 "$file" | dmenu -l 20) + +if test x"$line" != x""; then + printf '%s\n' "$line" +fi diff --git a/sob.1 b/sob.1 @@ -0,0 +1,33 @@ +.TH SOB 1 sob\-VERSION +.SH NAME +sob \- simple output bar +.SH SYNOPSIS +.B sob +.RB < \-f +.IR outfile > +.RB [ \-l +.IR line ] +.RB [ \-p +.IR prompt ] +.RB [ \-r +.IR resizecmd ] +.SH DESCRIPTION +sob is a simple line editor. +.SH OPTIONS +.TP +.B \-f " outfile" +output file. +.TP +.B \-i " id" +extra parameter passed to program. +.TP +.B \-l " line" +initial input on line. +.TP +.B \-p " prompt" +prompt string. +.TP +.B \-r " resizecmd" +command to run on SIGWINCH (ncurses KEY_RESIZE). +.SH BUGS +Please report them! diff --git a/sob.c b/sob.c @@ -0,0 +1,593 @@ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <locale.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/select.h> +#include <unistd.h> + +#include <ncurses.h> + +#include "arg.h" +char *argv0; + +#include "util.h" + +#define CONTROL(ch) ((ch)^0x40) +#define LEN(x) (sizeof (x) / sizeof *(x)) +#define MAX(A, B) ((A) > (B) ? (A) : (B)) + +struct line { + char line[BUFSIZ]; + size_t len; + size_t pos; +}; + +static struct line line; +static int isrunning = 1; +static char * outname = NULL; +static WINDOW * win = NULL; + +static void line_clear(void); +static void line_copywordcursor(char *buf, size_t bufsiz); +static void line_cursor_begin(void); +static void line_cursor_end(void); +static void line_cursor_next(void); +static void line_cursor_prev(void); +static void line_cursor_update(void); +static void line_delcharback(void); +static void line_delcharnext(void); +static void line_deltoend(void); +static void line_delwordback(void); +static void line_delwordcursor(void); +static void line_exit(void); +static void line_getwordpos(unsigned int *start, unsigned int *end); +static void line_insertchar(int c); +static void line_inserttext(const char *s); +static void line_newline(void); +static int line_out(void); +static void line_prompt(void); +static int line_pipeto(char **cmd); +static void line_redraw(size_t max); +static void line_set(const char *s); +static void line_yank(void); +static void history_menu(void); +static void complete_word(void); +static int pipereadline(int fd_in, int fd_out, char *writestr, char *outbuf, + size_t outbufsiz); +static int pipecmd(char *cmd[], char *writestr, char *outbuf, + size_t outbufsiz); +static void sighandler(int signum); +static void setup(void); +static void cleanup(void); +static void run(void); +static void usage(void); + +#include "config.h" + +static void +line_insertchar(int c) +{ + char s[2]; + + s[0] = c; + s[1] = '\0'; + line_inserttext(s); +} + +static void +line_inserttext(const char *s) +{ + size_t len; + + len = strlen(s); + if(line.pos + len + 1 > sizeof(line.line)) + return; + /* append */ + if(line.pos == line.len) { + memmove(&line.line[line.pos], s, len); + } else { + /* insert */ + memmove(&line.line[line.pos + len], &line.line[line.pos], line.len - line.pos); + memcpy(&line.line[line.pos], s, len); + } + line.pos += len; + line.len += len; + line.line[line.len + 1] = '\0'; +} + +static void +line_set(const char *s) { + strlcpy(line.line, s, sizeof(line.line)); + line.len = strlen(line.line); + line.pos = line.len; +} + +static void +line_prompt(void) +{ + size_t i; + + wmove(win, 0, 0); + for(i = 0; prompt[i]; i++) + waddch(win, prompt[i]); +} + +static void +line_redraw(size_t max) +{ + size_t n; + + line_prompt(); + + for(n = 0; line.line[n] && n < line.len && n < max; n++) + waddch(win, line.line[n]); + for(; n < max; n++) + waddch(win, ' '); + wrefresh(win); +} + +static int +line_out(void) +{ + FILE *fp; + if(!(fp = fopen(outname, "a"))) { + fprintf(stderr, "fopen: '%s': %s\n", outname, strerror(errno)); + return -1; + } + fprintf(fp, "%s\n", line.line); + fflush(fp); + fclose(fp); + return 0; +} + +static void +line_cursor_update(void) +{ + wmove(win, 0, line.pos + strlen(prompt)); +} + +static void +line_cursor_begin(void) +{ + line.pos = 0; + line_cursor_update(); +} + +static void +line_cursor_prev(void) +{ + if(line.pos > 0) + line.pos--; + line_cursor_update(); +} + +static void +line_cursor_next(void) +{ + if(line.pos < line.len) + line.pos++; + line_cursor_update(); +} + +static void +line_cursor_end(void) +{ + line.pos = line.len; + line_cursor_update(); +} + +static void +line_clear(void) +{ + line.line[0] = '\0'; + line_redraw(line.len); + line.len = 0; + line_cursor_begin(); +} + +static void +line_delcharnext(void) +{ + size_t oldlen = line.len; + + if(line.pos == line.len || line.len <= 0) + return; + + memmove(&line.line[line.pos], &line.line[line.pos + 1], + line.line[line.len - line.pos - 1]); + line.len--; + line.line[line.len] = '\0'; + line_redraw(oldlen); + line_cursor_update(); +} + +static void +line_delcharback(void) +{ + size_t oldlen = line.len; + + if(line.pos <= 0 || line.len <= 0) + return; + memmove(&line.line[line.pos - 1], &line.line[line.pos], + line.line[line.len - line.pos]); + line.len--; + line.line[line.len] = '\0'; + line_redraw(oldlen); + line_cursor_prev(); +} + +static void +line_deltoend(void) +{ + size_t oldlen = line.len; + + line.line[line.pos] = '\0'; + line.len = line.pos; + line_redraw(oldlen); + line_cursor_end(); +} + +static void +line_delwordcursor(void) +{ + unsigned int s, e; + size_t len, oldlen = line.len; + + line_getwordpos(&s, &e); + + memmove(&line.line[s], &line.line[e], line.len - e); + len = e - s; + line.len -= len; + line.pos = s; + line.line[line.len] = '\0'; + line_redraw(MAX(line.len, oldlen)); + line_cursor_update(); +} + +static void +line_delwordback(void) +{ + size_t i, len, oldlen = line.len; + + if(line.pos <= 0 || line.len <= 0) + return; + + i = line.pos; + while(i > 0 && isspace(line.line[i - 1])) + i--; + while(i > 0 && !isspace(line.line[i - 1])) + i--; + + len = line.len - line.pos; + if(len > 0) + memmove(&line.line[i], &line.line[line.pos], + line.len - line.pos); + len = line.pos - i; + line.pos = i; + line.len -= len; + line.line[line.len] = '\0'; + line_redraw(oldlen); + line_cursor_update(); +} + +static void +line_newline(void) +{ + line_out(); + line_clear(); + line_prompt(); + wrefresh(win); +} + +static void +line_exit(void) +{ + line_newline(); + isrunning = 0; +} + +static void +line_getwordpos(unsigned int *start, unsigned int *end) +{ + size_t i; + + i = line.pos; + while(i > 0 && !isspace(line.line[i - 1])) + i--; + if(start) + *start = i; + i = line.pos; + while(line.line[i] && i < line.len && !isspace(line.line[i])) + i++; + if(end) + *end = i; +} + +static void +line_copywordcursor(char *buf, size_t bufsiz) +{ + unsigned int s, e; + size_t len; + + line_getwordpos(&s, &e); + len = e - s; + /* truncate */ + if(len + 1 > bufsiz) + len = bufsiz - 1; + memcpy(buf, &line.line[s], len); + buf[len + 1] = '\0'; +} + +static void +complete_word(void) +{ + char wordbuf[BUFSIZ], outbuf[BUFSIZ]; + size_t oldlen = line.len; + + outbuf[0] = '\0'; + line_copywordcursor(wordbuf, sizeof(wordbuf)); + + if(pipecmd((char**)completewordcmd, wordbuf, outbuf, + sizeof(outbuf)) == -1) + return; + if(outbuf[0] == '\0') + return; + + line_delwordcursor(); + line_inserttext(outbuf); + line_redraw(MAX(line.len, oldlen)); + line_cursor_update(); +} + +static void +line_yank(void) +{ + line_pipeto((char**)yankcmd); +} + +static void +history_menu(void) +{ + line_pipeto((char**)historycmd); +} + +static int +pipereadline(int fd_in, int fd_out, char *writestr, char *outbuf, + size_t outbufsiz) +{ + char buf[PIPE_BUF], *p; + struct timeval tv; + fd_set fdr, fdw; + int r, w, maxfd, status = -1, haswritten = 0; + + for(;;) { + FD_ZERO(&fdr); + FD_ZERO(&fdw); + if(haswritten) { + FD_SET(fd_in, &fdr); + maxfd = fd_in; + } else { + FD_SET(fd_out, &fdw); + maxfd = fd_out; + } + memset(&tv, 0, sizeof(tv)); + tv.tv_sec = 0; + tv.tv_usec = 200; + + if((r = select(maxfd + 1, haswritten ? &fdr : NULL, + haswritten ? NULL : &fdw, NULL, &tv)) == -1) + goto fini; + else if(!r) /* timeout */ + continue; + + if(haswritten) { + if(FD_ISSET(fd_in, &fdr)) { + /* read until newline */ + if((r = read(fd_in, buf, sizeof(buf))) == -1) + goto fini; + buf[r] = '\0'; + if((p = strpbrk(buf, "\r\n"))) + *p = '\0'; + strlcpy(outbuf, buf, sizeof(outbuf)); + status = 0; + goto fini; + } + } else { + if(FD_ISSET(fd_out, &fdw)) { + /* write error */ + if((w = write(fd_out, writestr, strlen(writestr))) == -1) + goto fini; + close(fd_out); /* sends EOF */ + haswritten = 1; + } + } + } +fini: + close(fd_in); + close(fd_out); + return status; +} + +static int +pipecmd(char *cmd[], char *writestr, char *outbuf, size_t outbufsiz) +{ + struct sigaction sa; + pid_t pid; + int pc[2], cp[2]; + + if ((pipe(pc) == -1) || (pipe(cp) == -1)) { + perror("pipe"); + return -1; + } + pid = fork(); + if (pid == -1) { + perror("fork"); + return -1; + } else if (pid == 0) { + /* child */ + close(cp[0]); + close(pc[1]); + if (dup2(pc[0], STDIN_FILENO) == -1 || + dup2(cp[1], STDOUT_FILENO) == -1) { + perror("dup2"); + return -1; + } + if(execv(cmd[0], (char**)cmd) == -1) { + perror("execv"); + _exit(EXIT_FAILURE); /* NOTE: must be _exit */ + } + _exit(EXIT_SUCCESS); + } else { + /* parent */ + close(pc[0]); + close(cp[1]); + + /* ignore SIGPIPE, we handle this for write(). */ + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &sa, NULL); + + if(pipereadline(cp[0], pc[1], writestr, outbuf, outbufsiz) == -1) + return -1; + } + return 0; +} + +static int +line_pipeto(char **cmd) +{ + size_t oldlen = line.len; + + if(pipecmd(cmd, line.line, line.line, sizeof(line.line)) == -1) + return -1; + line.len = strlen(line.line); + line_redraw(MAX(line.len, oldlen)); + line_cursor_end(); + return 0; +} + +static void +sighandler(int signum) +{ + if(signum == SIGTERM) { + isrunning = 0; + cleanup(); + } +} + +static void +setup(void) +{ + struct sigaction sa; + + initscr(); + win = stdscr; + cbreak(); + noecho(); + nonl(); + nodelay(win, FALSE); + keypad(win, TRUE); + curs_set(1); + ESCDELAY = 20; + + /* signal handling */ + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESTART; + sa.sa_handler = sighandler; + sigaction(SIGTERM, &sa, NULL); +} + +static void +cleanup(void) +{ + endwin(); +} + +static void +run(void) +{ + size_t i; + int c, ismatch = 0; + + line_redraw(line.len); + while(isrunning) { + c = wgetch(win); + switch(c) { + case ERR: + isrunning = 0; + break; + case 0x1b: /* ignore unbinded escape sequences */ + nodelay(win, TRUE); + while((c = wgetch(win)) != ERR); + nodelay(win, FALSE); + break; + case KEY_RESIZE: + if(resizecmd && resizecmd[0]) + system(resizecmd); + break; + default: + ismatch = 0; + for(i = 0; i < LEN(keybinds); i++) { + if(keybinds[i].key == c) { + ismatch = 1; + keybinds[i].func(); + break; + } + } + if(!ismatch) { + line_insertchar(c); + line_redraw(line.len); + line_cursor_update(); + wrefresh(win); + } + } + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s <-f outfile> [-i id] [-l line] [-p prompt] " + "[-r resizecmd]\n", argv0); + exit(EXIT_FAILURE); +} + +int +main(int argc, char **argv) +{ + ARGBEGIN { + case 'f': + outname = EARGF(usage()); + break; + case 'i': + strlcpy(id, EARGF(usage()), sizeof(id)); + break; + case 'l': + line_set(EARGF(usage())); + break; + case 'p': + prompt = EARGF(usage()); + break; + case 'r': + resizecmd = EARGF(usage()); + break; + default: + usage(); + } ARGEND; + + if(!outname) + usage(); + + setlocale(LC_ALL, ""); + setup(); + run(); + cleanup(); + + return EXIT_SUCCESS; +} diff --git a/strlcpy.c b/strlcpy.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <string.h> +#include "util.h" + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + return(s - src - 1); /* count does not include NUL */ +} diff --git a/util.h b/util.h @@ -0,0 +1,5 @@ +#include <stdio.h> +#include <unistd.h> + +#undef strlcpy +size_t strlcpy(char *dst, const char *src, size_t siz);