aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-02-08 21:39:42 +0100
committerNathan Reiner <nathan@nathanreiner.xyz>2023-02-08 21:39:42 +0100
commit764f8d22b250e09c1dea98fb8073e71a720892f7 (patch)
tree8357f9f673846125ac49ff0f12288d242eb2fa49
parentb97ec93748902c90f25524116d3d189f1b263474 (diff)
add pager
-rw-r--r--core/pager.c109
-rw-r--r--lib/cstr/cstr.c50
-rw-r--r--lib/cstr/cstr.h9
-rw-r--r--lib/io/io.c5
-rw-r--r--lib/io/io.h6
-rw-r--r--lib/tctl/tctl.c40
-rw-r--r--lib/tctl/tctl.h9
7 files changed, 220 insertions, 8 deletions
diff --git a/core/pager.c b/core/pager.c
new file mode 100644
index 0000000..cda5300
--- /dev/null
+++ b/core/pager.c
@@ -0,0 +1,109 @@
+#include "../lib/container/container.h"
+#include "../lib/io/io.h"
+#include "../lib/aec/aec.h"
+#include "../lib/tctl/tctl.h"
+#include "../lib/cstr/cstr.h"
+
+
+#define BUFFER_SIZE 128
+char buf[BUFFER_SIZE];
+container_t *string;
+
+
+u64 scroll = 0;
+u64 maxscroll = 0;
+u64 num_lines = 0;
+termios_t original;
+termios_t current;
+
+
+void scroll_delta(int d)
+{
+ scroll += d;
+
+ if ((i64)(scroll) < 0) {
+ scroll = 0;
+ } else if (scroll > maxscroll) {
+ scroll = maxscroll;
+ }
+}
+
+void draw_screen()
+{
+ window_size_t ws = tctl_get_window_size();
+ clear_screen();
+ move_cursor(0, 0);
+
+ substr_t line = getline(string->data, scroll);
+
+ for (int i = 0; i < ws.height - 1 && line.begin; ++i) {
+ write(STDOUT_FD, line.begin, line.end - line.begin + 1);
+ line = nextline(line);
+ }
+
+ move_cursor(0, ws.height);
+ wstdf("%S Line: %i/%i %S", SGR_REVERSE, scroll + 1, num_lines, SGR_REVERSE_OFF);
+
+ flush(STDOUT_FD);
+}
+
+
+int main(int argc, const char **argv)
+{
+ if (argc != 1) {
+ wf(STDERR_FD, "pager\n");
+ return -1;
+ }
+
+ if (!isatty()) {
+ wf(STDERR_FD, "error: stdout is no tty! exiting.\n");
+ return -1;
+ }
+
+ string = new_container(sizeof(char));
+ u64 size;
+
+ while ((size = rstd(buf, BUFFER_SIZE))) {
+ container_append(string, buf, size);
+ }
+
+ container_push(string, 0);
+
+ tcgetattr(STDOUT_FD, &original);
+ current = original;
+ setcanonical(&current, 0);
+ setecho(&current, 0);
+ cursor_enabled(0);
+ tcsetattr(STDOUT_FD, &current);
+
+
+ char key;
+ int running = 1;
+ window_size_t ws = tctl_get_window_size();
+ num_lines = number_of_lines(string->data);
+ maxscroll = num_lines < ws.height ? 0 : num_lines - ws.height + 1;
+
+ draw_screen();
+
+ while (running && read(STDERR_FD, &key, 1) == 1) {
+ switch (key) {
+ case 'j': scroll_delta(1); break;
+ case 'k': scroll_delta(-1); break;
+ case 'g': scroll = 0; break;
+ case 'G': scroll = maxscroll; break;
+
+ case 'q':
+ case '\033': running = 0; break;
+ }
+
+ draw_screen();
+ }
+
+ tcsetattr(STDOUT_FD, &original);
+ cursor_enabled(1);
+
+ clear_screen();
+ move_cursor(0, 0);
+
+ return 0;
+}
diff --git a/lib/cstr/cstr.c b/lib/cstr/cstr.c
index f464cf3..9f4ad6f 100644
--- a/lib/cstr/cstr.c
+++ b/lib/cstr/cstr.c
@@ -175,6 +175,50 @@ u64 strip_cstr(char *cstr, char strip)
return strip_front(cstr, strip) + strip_back(cstr, strip);
}
+
+u64 number_of_lines(const char *cstr)
+{
+ u64 lines = 0;
+
+ while (*cstr) {
+ if (*cstr == '\n') ++lines;
+ ++cstr;
+ }
+
+ return lines;
+}
+
+
+substr_t getline(const char *cstr, u64 n)
+{
+ substr_t line;
+ line.begin = (char *)cstr;
+ line.end = line.begin;
+
+ while (*line.end && *line.end != '\n') ++line.end;
+
+ for (int i = 0; i < n; ++i)
+ line = nextline(line);
+
+ return line;
+}
+
+
+substr_t nextline(substr_t line)
+{
+ if (!(*line.end || *(line.end + 1))) {
+ line.begin = 0;
+ line.end = 0;
+ return line;
+ }
+
+ line.begin = ++line.end;
+
+ while (*line.end && *line.end != '\n') ++line.end;
+
+ return line;
+}
+
/* DOC
* @type function
* @name string_utf8_length
@@ -272,6 +316,12 @@ void u64_to_cstr(u64 n, char *cstr, u64 length)
n /= 10;
}
+ if (p == cstr + length - 1) {
+ *(p--) = '0';
+ }
+
+ *p = 0;
+
d = p - cstr + 1;
for (i = 0; i < length - d; ++i)
diff --git a/lib/cstr/cstr.h b/lib/cstr/cstr.h
index 80dc359..ce27eb0 100644
--- a/lib/cstr/cstr.h
+++ b/lib/cstr/cstr.h
@@ -3,6 +3,11 @@
#include "../sys/sizes.h"
+typedef struct {
+ char *begin;
+ char *end;
+} substr_t;
+
u64 cstr_length(const char *);
i8 cstr_compare(const char *a, const char *b);
u64 cstr_split(char *cstr, char split);
@@ -10,6 +15,10 @@ const char *next_split(const char *previous);
u64 strip_front(char *cstr, char strip);
u64 strip_back(char *cstr, char strip);
u64 strip_cstr(char *cstr, char strip);
+u64 number_of_lines(const char *cstr);
+
+substr_t getline(const char *cstr, u64 n);
+substr_t nextline(substr_t line);
u64 cstr_utf8_length(const char *);
u8 next_utf8(const char **);
diff --git a/lib/io/io.c b/lib/io/io.c
index 7dee314..57cfd05 100644
--- a/lib/io/io.c
+++ b/lib/io/io.c
@@ -15,9 +15,9 @@ void wstd(const char *buf)
}
-void rstd(char *buf, unsigned long count)
+unsigned long rstd(char *buf, unsigned long count)
{
- read(STDIN_FD, buf, count);
+ return read(STDIN_FD, buf, count);
}
@@ -26,6 +26,7 @@ void wf(unsigned int fd, char *buf)
write(fd, buf, cstr_length(buf));
}
+
void wstdf__(const char *buf, const void **args)
{
wff__(STDOUT_FD, buf, args);
diff --git a/lib/io/io.h b/lib/io/io.h
index c373cd0..ac25e87 100644
--- a/lib/io/io.h
+++ b/lib/io/io.h
@@ -3,9 +3,9 @@
#include "../sys/io.h"
-void wstd(const char *buf);
-void rstd(char *buf, unsigned long count);
-void wf(unsigned int fd, char *buf);
+void wstd(const char *buf);
+unsigned long rstd(char *buf, unsigned long count);
+void wf(unsigned int fd, char *buf);
void wstdf__(const char *buf, const void **args);
void wff__(unsigned int fd, const char *buf, const void **args);
diff --git a/lib/tctl/tctl.c b/lib/tctl/tctl.c
index 187d1a9..d54f2dd 100644
--- a/lib/tctl/tctl.c
+++ b/lib/tctl/tctl.c
@@ -4,6 +4,10 @@
#define GET_WIN_SIZE 0x5413
#define SET_WIN_SIZE 0x5414
#define TC_GET_ATTR 0x5401
+#define TC_SET_ATTR 0x5402
+#define FD_FLUSH 0x000b
+#define ICANON 000002
+#define ECHO 000010
window_size_t tctl_get_window_size()
{
@@ -24,3 +28,39 @@ int isatty()
termios_t term;
return ioctl(1, TC_GET_ATTR, &term) == 0;
}
+
+
+int tcgetattr(int fd, termios_t *termios)
+{
+ return ioctl(fd, TC_GET_ATTR, termios);
+}
+
+
+void tcsetattr(int fd, termios_t *termios)
+{
+ ioctl(fd, TC_SET_ATTR, termios);
+}
+
+
+void setcanonical(termios_t *term, int is_canonical)
+{
+ if (is_canonical)
+ term->c_lflag |= ICANON;
+ else
+ term->c_lflag &= ~ICANON;
+}
+
+
+void setecho(termios_t *term, int is_echo)
+{
+ if (is_echo)
+ term->c_lflag |= ECHO;
+ else
+ term->c_lflag &= ~ECHO;
+}
+
+
+void flush(unsigned int fd)
+{
+ ioctl(fd, FD_FLUSH, 0);
+}
diff --git a/lib/tctl/tctl.h b/lib/tctl/tctl.h
index bdcdb66..d16c9eb 100644
--- a/lib/tctl/tctl.h
+++ b/lib/tctl/tctl.h
@@ -14,13 +14,16 @@ typedef struct {
unsigned int c_cflag;
unsigned int c_lflag;
unsigned char c_line;
- unsigned char c_cc[32];
- unsigned int c_ispeed;
- unsigned int c_ospeed;
+ unsigned char c_cc[19];
} termios_t;
window_size_t tctl_get_window_size();
void tctl_set_window_size(window_size_t size);
int isatty();
+int tcgetattr(int fd, termios_t *termios);
+void tcsetattr(int fd, termios_t *termios);
+void setcanonical(termios_t *term, int is_canonical);
+void setecho(termios_t *term, int is_echo);
+void flush(unsigned int fd);
#endif