Introduction
Introduction Statistics Contact Development Disclaimer Help
rework some things: - sob - simple output bar
git clone git://git.codemadness.org/sob
Log
Files
Refs
README
LICENSE
---
commit 12656a030acf296f0b6299175f982a6eae630646
parent b072bb3bd5fdff06b0a12cd7bc6a8f94962ab97d
Author: Hiltjo Posthuma <[email protected]>
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 <[email protected]>, 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 hi…
- 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/…
static const char *historycmd[] = { "/bin/sh", "-c", "$HOME/.sob/scripts/…
static const char *yankcmd[] = { "/bin/sh", "-c", "/bin/xsel -i -p", …
@@ -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…
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 …
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_…
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", argv…
+ 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();
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.