commit 12656a030acf296f0b6299175f982a6eae630646
parent b072bb3bd5fdff06b0a12cd7bc6a8f94962ab97d
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Thu, 2 Oct 2014 20:55:49 +0000
rework some things:
- remove dependency on ncurses.
- just use stdout for output instead of -f, this makes using it with tee
trivial. use stderr for the "interface".
- fix resize bug select() == 1 with errno EINTR.
Diffstat:
M | README | | | 24 | ++++++++++++++++++------ |
M | TODO | | | 25 | +++++-------------------- |
M | config.def.h | | | 74 | ++++++++++++++++++++++++++++++++++++++++++-------------------------------- |
M | sob.1 | | | 5 | ----- |
M | sob.c | | | 417 | ++++++++++++++++++++++++++++++++++++++++++++++++------------------------------- |
5 files changed, 318 insertions(+), 227 deletions(-)
diff --git a/README b/README
@@ -1,25 +1,37 @@
-Simple output bar
-=================
+sob - simple output bar
+=======================
Dependencies
------------
-- Ncurses (at the moment, alternatives will be investigated).
- libc
Features
--------
-- Custom prompt.
-- Easy to write scripts to pipe input/output.
+- Small (in size and memory), not much dependencies.
+- Custom prompt (including color support).
+- Easy to write custom completion scripts or special actions.
- Custom action on SIGWINCH (window resize).
- Familiar and customizable keybinds (in config.h).
- Example scripts for:
+ - History list.
+ - Nickname completion (for IRC).
- Word completion.
- - History
- Yank line (xsel).
+Known issues
+------------
+line yank doesn't work with xclip, but does work with xsel.
+
+
+Author
+------
+Hiltjo Posthuma <hiltjo@codemadness.nl>, don't hesitate to contact me for
+bug reports or patches!
+
+
License
-------
See LICENSE file.
diff --git a/TODO b/TODO
@@ -1,22 +1,7 @@
-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
-
+- tmux: on newline, sometimes there are render issues.
+- tmux: arrow up and down dont start history script.
+- for history, on exit failure, don't clear line.
- scripts: for word complete, if there is only one match, just print directly.
-
-- 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.
+ reduce redraws (line_redraw and line_cursor_update).
+- make example url grab script.
diff --git a/config.def.h b/config.def.h
@@ -1,4 +1,4 @@
-static const char *prompt = "> ";
+static const char *prompt = "\x01\x1b[32m\x01> \x01\x1b[0m";
static const char *completenickcmd[] = { "/bin/sh", "-c", "$HOME/.sob/scripts/complete_nick", NULL };
static const char *historycmd[] = { "/bin/sh", "-c", "$HOME/.sob/scripts/history", NULL };
static const char *yankcmd[] = { "/bin/sh", "-c", "/bin/xsel -i -p", NULL };
@@ -22,38 +22,48 @@ complete_nick(void)
line_wordpipeto((char**)completenickcmd);
}
+#define CONTROL(ch) ((ch)^0x40)
+
+#define KEY_HOME "\x1b[\x31\x7e"
+#define KEY_END "\x1b[\x34\x7e"
+#define KEY_CTRL_LEFT "\x1b\x5b\x31\x3b\x35\x44"
+#define KEY_CTRL_RIGHT "\x1b\x5b\x31\x3b\x35\x43"
+#define KEY_LEFT "\x1b\x4f\x44"
+#define KEY_RIGHT "\x1b\x4f\x43"
+#define KEY_DOWN "\x1b\x4f\x42"
+#define KEY_UP "\x1b\x4f\x41"
+#define KEY_DC "\x1b\x5b\x33\7e" /* del */
+
static struct keybind {
- int key;
+ unsigned char key[16];
void (*func)(void);
} keybinds[] = {
- { 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_nick },
+ { { 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 },
+ { { KEY_CTRL_LEFT }, line_cursor_wordprev },
+ { { KEY_CTRL_RIGHT }, line_cursor_wordnext },
+ { { CONTROL('W') }, line_delwordback },
+ { { CONTROL('H') }, line_delcharback },
+ { { CONTROL('U') }, line_clear },
+ { { CONTROL('K') }, line_deltoend },
+ { { KEY_DC }, line_delcharnext },
+ { { CONTROL('H') }, line_delcharback },
+ { { CONTROL('M') }, line_newline },
+ { { CONTROL('J') }, line_newline },
+ { { '\r' }, line_newline },
+ { { '\n' }, line_newline },
+ { { CONTROL('Y') }, line_yank },
+ { { CONTROL('D') }, line_exit },
+ { { CONTROL('E') }, line_deltoend },
+ { { KEY_UP }, history_menu },
+ { { KEY_DOWN }, history_menu },
+ { { CONTROL('P') }, history_menu },
+ { { CONTROL('N') }, history_menu },
+ { { '\t' }, complete_nick },
};
diff --git a/sob.1 b/sob.1
@@ -3,8 +3,6 @@
sob \- simple output bar
.SH SYNOPSIS
.B sob
-.RB < \-f
-.IR outfile >
.RB [ \-l
.IR line ]
.RB [ \-p
@@ -13,9 +11,6 @@ sob \- simple output bar
sob is a simple line editor.
.SH OPTIONS
.TP
-.B \-f " outfile"
-output file.
-.TP
.B \-l " line"
initial input on line.
.TP
diff --git a/sob.c b/sob.c
@@ -7,19 +7,19 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
#include <sys/select.h>
#include <unistd.h>
-
-#include <ncurses.h>
+#include <termios.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))
+#define LEN(x) (sizeof (x) / sizeof *(x))
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
struct line {
char line[BUFSIZ];
@@ -27,58 +27,54 @@ struct line {
size_t pos;
};
+static void line_clear(void);
+static void line_copywordcursor(char *, size_t);
+static void line_cursor_begin(void);
+static void line_cursor_end(void);
+static void line_cursor_move(size_t);
+static void line_cursor_next(void);
+static void line_cursor_prev(void);
+static void line_cursor_wordprev(void);
+static void line_cursor_wordnext(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_draw(void);
+static void line_exit(void);
+static void line_getwordpos(size_t *, size_t *);
+static void line_inserttext(const char *);
+static void line_newline(void);
+static void line_out(void);
+static void line_prompt(void);
+static int line_promptlen(void);
+static int line_pipeto(char **);
+static void line_set(const char *);
+static void line_wordpipeto(char **);
+static int pipe_readline(int, int, char *, char *, size_t);
+static int pipe_cmd(char *[], char *, char *, size_t);
+
+static void cleanup(void);
+static void gettermsize(void);
+static void handleinput(const unsigned char *, size_t);
+static void resize(void);
+static int run(void);
+static void setup(void);
+static void sighandler(int);
+static void usage(void);
+
+static struct termios ttystate, ttysave;
+
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_wordpipeto(char **cmd);
-static int pipe_readline(int fd_in, int fd_out, char *writestr,
- char *outbuf, size_t outbufsiz);
-static int pipe_cmd(char *cmd[], char *writestr, char *outbuf,
- size_t outbufsiz);
-static void resize(void);
-static void sighandler(int signum);
-static void setup(void);
-static void cleanup(void);
-static void run(void);
-static void usage(void);
+static int cols, rows;
+static int isrunning = 1;
+static FILE * outfp = NULL;
+static FILE * lineoutfp = NULL;
#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;
@@ -94,9 +90,10 @@ line_inserttext(const char *s)
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.pos += len;
line.line[line.len + 1] = '\0';
+ line_draw();
}
static void
@@ -107,94 +104,137 @@ line_set(const char *s)
line.pos = line.len;
}
+/* like mksh, toggle counting of escape codes in prompt with "\x01" */
+static int
+line_promptlen(void)
+{
+ size_t i;
+ int t = 0, n = 0;
+
+ for(i = 0; prompt[i]; i++) {
+ if(prompt[i] == 1)
+ t = !t;
+ else if(!t)
+ n++;
+ }
+ return n;
+}
+
static void
line_prompt(void)
{
size_t i;
- wmove(win, 0, 0);
- for(i = 0; prompt[i]; i++)
- waddch(win, prompt[i]);
+ for(i = 0; prompt[i]; i++) {
+ if(prompt[i] != 1)
+ fputc(prompt[i], outfp);
+ }
}
static void
-line_redraw(size_t max)
+line_draw(void)
{
size_t n;
+ /* clear */
+ fprintf(outfp, "\x1b[2J\x1b[H");
+
line_prompt();
+ for(n = 0; line.line[n] && n < line.len; n++)
+ fputc(line.line[n], outfp);
- 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);
+ line_cursor_move(line.pos);
}
-static int
+static void
line_out(void)
{
- FILE *fp;
- if(!(fp = fopen(outname, "a"))) {
- fprintf(stderr, "fopen: '%s': %s\n", outname, strerror(errno));
- return -1;
+ fprintf(lineoutfp, "%s\n", line.line);
+ fflush(lineoutfp);
+}
+
+static void
+line_cursor_move(size_t newpos)
+{
+ size_t len, y = 0, x = newpos;
+
+ len = line_promptlen();
+ x += len;
+
+ /* linewrap */
+ if(x > cols - 1) {
+ x = x % cols;
+ y = ((newpos + len) - x) / cols;
}
- fprintf(fp, "%s\n", line.line);
- fflush(fp);
- fclose(fp);
- return 0;
+ fprintf(outfp, "\x1b[%lu;%luH", y + 1, x + 1);
+ fflush(outfp);
+ line.pos = newpos;
+}
+
+static void
+line_cursor_wordprev(void)
+{
+ size_t s, e;
+
+ line_getwordpos(&s, &e);
+ if(s == line.pos) {
+ while(s > 0 && isspace(line.line[s - 1]))
+ s--;
+ }
+ line_cursor_move(s);
}
static void
-line_cursor_update(void)
+line_cursor_wordnext(void)
{
- wmove(win, 0, line.pos + strlen(prompt));
+ size_t s, e;
+
+ line_getwordpos(&s, &e);
+ if(e == line.pos) {
+ while(e < line.len && line.line[e] && isspace(line.line[e]))
+ e++;
+ }
+ line_cursor_move(e);
}
static void
line_cursor_begin(void)
{
- line.pos = 0;
- line_cursor_update();
+ line_cursor_move(0);
}
static void
line_cursor_prev(void)
{
if(line.pos > 0)
- line.pos--;
- line_cursor_update();
+ line_cursor_move(line.pos - 1);
}
static void
line_cursor_next(void)
{
if(line.pos < line.len)
- line.pos++;
- line_cursor_update();
+ line_cursor_move(line.pos + 1);
}
static void
line_cursor_end(void)
{
- line.pos = line.len;
- line_cursor_update();
+ line_cursor_move(line.len);
}
static void
line_clear(void)
{
+ line_cursor_begin();
line.line[0] = '\0';
- line_redraw(line.len);
line.len = 0;
- line_cursor_begin();
+ line_draw();
}
static void
line_delcharnext(void)
{
- size_t oldlen = line.len;
-
if(line.pos == line.len || line.len <= 0)
return;
@@ -202,41 +242,35 @@ line_delcharnext(void)
line.line[line.len - line.pos - 1]);
line.len--;
line.line[line.len] = '\0';
- line_redraw(oldlen);
- line_cursor_update();
+ line_draw();
}
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();
+ line_draw();
}
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();
+ line_draw();
}
static void
line_delwordcursor(void)
{
- unsigned int s, e;
- size_t len, oldlen = line.len;
+ size_t len, s, e;
line_getwordpos(&s, &e);
@@ -245,14 +279,13 @@ line_delwordcursor(void)
line.len -= len;
line.pos = s;
line.line[line.len] = '\0';
- line_redraw(MAX(line.len, oldlen));
- line_cursor_update();
+ line_draw();
}
static void
line_delwordback(void)
{
- size_t i, len, oldlen = line.len;
+ size_t i, len;
if(line.pos <= 0 || line.len <= 0)
return;
@@ -271,8 +304,7 @@ line_delwordback(void)
line.pos = i;
line.len -= len;
line.line[line.len] = '\0';
- line_redraw(oldlen);
- line_cursor_update();
+ line_draw();
}
static void
@@ -280,19 +312,17 @@ line_newline(void)
{
line_out();
line_clear();
- line_prompt();
- wrefresh(win);
}
static void
line_exit(void)
{
- line_newline();
+ line_out();
isrunning = 0;
}
static void
-line_getwordpos(unsigned int *start, unsigned int *end)
+line_getwordpos(size_t *start, size_t *end)
{
size_t i;
@@ -311,8 +341,7 @@ line_getwordpos(unsigned int *start, unsigned int *end)
static void
line_copywordcursor(char *buf, size_t bufsiz)
{
- unsigned int s, e;
- size_t len;
+ size_t s, e, len;
line_getwordpos(&s, &e);
len = e - s;
@@ -358,9 +387,11 @@ pipe_readline(int fd_in, int fd_out, char *writestr, char *outbuf,
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));
+ if(outbuf) {
+ if((p = strpbrk(buf, "\r\n")))
+ *p = '\0';
+ strlcpy(outbuf, buf, outbufsiz);
+ }
status = 0;
goto fini;
}
@@ -434,21 +465,25 @@ pipe_cmd(char *cmd[], char *writestr, char *outbuf, size_t outbufsiz)
static int
line_pipeto(char **cmd)
{
- size_t oldlen = line.len;
+ char buf[BUFSIZ];
+ size_t len;
- if(pipe_cmd(cmd, line.line, line.line, sizeof(line.line)) == -1)
+ if(pipe_cmd(cmd, line.line, buf, sizeof(buf)) == -1)
return -1;
- line.len = strlen(line.line);
- line_redraw(MAX(line.len, oldlen));
+ if(buf[0] == '\0')
+ return -1;
+ len = strlcpy(line.line, buf, sizeof(line.line));
+ line.len = len;
line_cursor_end();
+ line_draw();
return 0;
}
+/* pipe word under cursor and replace it */
static void
line_wordpipeto(char **cmd)
{
char wordbuf[BUFSIZ], outbuf[BUFSIZ];
- size_t oldlen = line.len;
outbuf[0] = '\0';
wordbuf[0] = '\0';
@@ -462,8 +497,7 @@ line_wordpipeto(char **cmd)
line_delwordcursor();
line_inserttext(outbuf);
- line_redraw(MAX(line.len, oldlen));
- line_cursor_update();
+ line_draw();
}
static void
@@ -472,6 +506,10 @@ sighandler(int signum)
if(signum == SIGTERM) {
isrunning = 0;
cleanup();
+ } else if(signum == SIGWINCH) {
+ gettermsize();
+ resize();
+ line_draw();
}
}
@@ -480,79 +518,134 @@ setup(void)
{
struct sigaction sa;
- initscr();
- win = stdscr;
- cbreak();
- noecho();
- nonl();
- nodelay(win, FALSE);
- keypad(win, TRUE);
- curs_set(1);
- ESCDELAY = 20;
+ tcgetattr(STDIN_FILENO, &ttystate);
+ ttysave = ttystate;
+ /* turn off canonical mode and echo */
+ ttystate.c_lflag &= ~(ICANON | ECHO);
+ ttystate.c_cc[VMIN] = 1;
+
+ /* set the terminal attributes */
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
+
+ /* get terminal window size */
+ gettermsize();
/* signal handling */
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_RESTART;
sa.sa_handler = sighandler;
sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGWINCH, &sa, NULL);
+}
+
+static void
+gettermsize(void)
+{
+ struct winsize w;
+
+ if(ioctl(STDIN_FILENO, TIOCGWINSZ, &w) == -1)
+ return;
+ cols = w.ws_col;
+ rows = w.ws_row;
}
static void
resize(void)
{
- line_pipeto((char **)resizecmd);
+ pipe_cmd((char **)resizecmd, line.line, NULL, 0);
}
static void
cleanup(void)
{
- endwin();
+ ttystate.c_lflag = ttysave.c_lflag;
+ /* set the terminal attributes. */
+ tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}
+#if 0
static void
-run(void)
+debuginput(const unsigned char *key, size_t len)
{
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:
- resize();
+ for(i = 0; i < len; i++)
+ fprintf(stdout, "\\x%2x", key[i]);
+ fputc('\n', stdout);
+ fflush(stdout);
+}
+#endif
+
+static void
+handleinput(const unsigned char *key, size_t len)
+{
+ size_t i;
+ int ismatch = 0;
+
+ if(key[0] == '\0')
+ return;
+ for(i = 0; i < LEN(keybinds); i++) {
+ if(len == strlen((char*)keybinds[i].key) &&
+ memcmp(key, keybinds[i].key, len) == 0) {
+ keybinds[i].func();
+ ismatch = 1;
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);
+ }
+ }
+ if(!ismatch) {
+ /* ignore unhandled escape sequence */
+ if(key[0] == '\x1b' || iscntrl(key[0]))
+ return;
+ line_inserttext((char*)key);
+ }
+}
+
+static int
+run(void)
+{
+ struct timeval tv;
+ fd_set fdr;
+ int fd_in, r, status = -1;
+ unsigned char buf[BUFSIZ];
+
+ line_draw();
+
+ fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
+ while(isrunning) {
+ FD_ZERO(&fdr);
+ FD_SET(STDIN_FILENO, &fdr);
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = 0;
+ tv.tv_usec = 50000; /* 50 ms */
+
+ errno = 0;
+ if((r = select(STDIN_FILENO + 1, &fdr, NULL, NULL, &tv)) == -1) {
+ if(errno != EINTR)
+ goto fini; /* E_INTR can happen on SIGWINCH */
+ } else if(!r) {
+ continue; /* timeout */
+ }
+
+ if(FD_ISSET(fd_in, &fdr)) {
+ errno = 0;
+ if((r = read(STDIN_FILENO, buf, sizeof(buf))) == -1) {
+ if(errno != EAGAIN && errno != EWOULDBLOCK)
+ goto fini;
+ } else {
+ buf[r] = '\0';
+ handleinput(buf, r);
}
}
}
+fini:
+ return status;
}
static void
usage(void)
{
- fprintf(stderr, "usage: %s <-f outfile> [-l line] [-p prompt]\n", argv0);
+ fprintf(stderr, "usage: %s [-l line] [-p prompt]\n", argv0);
exit(EXIT_FAILURE);
}
@@ -560,9 +653,6 @@ int
main(int argc, char **argv)
{
ARGBEGIN {
- case 'f':
- outname = EARGF(usage());
- break;
case 'l':
line_set(EARGF(usage()));
break;
@@ -573,9 +663,8 @@ main(int argc, char **argv)
usage();
} ARGEND;
- if(!outname)
- usage();
-
+ lineoutfp = stdout;
+ outfp = stderr;
setlocale(LC_ALL, "");
setup();
run();