tsex.c - sex - libtermbox based text editor
 (HTM) git clone git://z3bra.org/sex
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
       tsex.c (4948B)
       ---
            1 #include <sys/types.h>
            2 #include <sys/stat.h>
            3 #include <sys/mman.h>
            4 #include <sys/queue.h>
            5 #include <termbox.h>
            6 #include <unistd.h>
            7 #include <string.h>
            8 #include <stdlib.h>
            9 #include <stdio.h>
           10 #include <fcntl.h>
           11 #include <err.h>
           12 
           13 #include "arg.h"
           14 #include "sex.h"
           15 
           16 #define TAB_WIDTH 8
           17 
           18 typedef enum { UP, DOWN, RIGHT, LEFT } direction;
           19 
           20 struct line_s {
           21         char  *p;
           22         size_t len;
           23         size_t number;
           24         TAILQ_ENTRY(line_s) entries;
           25 };
           26 
           27 struct file_s {
           28         char  *path;
           29         char  *map;
           30         char  *p;
           31         size_t size;
           32         int    d;
           33         struct line_s *ln;
           34 };
           35 
           36 struct coord_s {
           37         int x;
           38         int y;
           39 };
           40 
           41 struct ui_s {
           42         struct line_s *top;
           43         struct line_s *cur;
           44         int width;
           45         int height;
           46         struct coord_s pos;
           47 };
           48 
           49 static struct file_s f;
           50 TAILQ_HEAD(line_s_head, line_s) head;
           51 
           52 void
           53 cleanup(void)
           54 {
           55         munmap(f.map, f.size);
           56         close(f.d);
           57         tb_shutdown();
           58 }
           59 
           60 void
           61 init_termbox(void)
           62 {
           63         int r;
           64 
           65         r = tb_init();
           66         if (r)
           67                 err(1, "tb_init()");
           68 }
           69 
           70 void
           71 open_file(char *path)
           72 {
           73         size_t i, last_eol;
           74         int d, ln = 1;
           75         struct stat s;
           76         struct line_s *tmp;
           77 
           78         f.path = path;
           79 
           80         d = open(f.path, O_RDWR);
           81         if (d < 0)
           82                 err(1, "open()");
           83 
           84         if (fstat(d, &s))
           85                 err(1, "fstat()");
           86 
           87         f.size = (size_t)s.st_size;
           88 
           89         f.map = mmap(NULL, f.size, PROT_READ | PROT_WRITE, MAP_PRIVATE, d, 0);
           90         if (f.map == MAP_FAILED)
           91                 err(1, "mmap()");
           92 
           93         f.p = f.map;
           94 
           95         TAILQ_INIT(&head);
           96 
           97         for (i = last_eol = 0; i < f.size; ++i) {
           98                 if (*(f.map + i) == '\n') {
           99                         tmp = malloc(sizeof(*tmp));
          100                         if (tmp == NULL)
          101                                 err(1, "malloc()");
          102 
          103                         tmp->p = f.map + last_eol;
          104                         tmp->len = i - last_eol + 1;
          105                         tmp->number = ln++;
          106                         last_eol = i + 1;
          107                         TAILQ_INSERT_TAIL(&head, tmp, entries);
          108                 }
          109         }
          110 }
          111 
          112 ssize_t
          113 ui_expand_tab(int x, int y, uint8_t tw)
          114 {
          115         uint8_t i, tabsize = 0;
          116 
          117         tabsize = tw - (x % tw);
          118         for (i=x; i<(x + tabsize); i++)
          119                 tb_change_cell(i, y, ' ', TB_DEFAULT, TB_DEFAULT);
          120 
          121         return tabsize;
          122 }
          123 
          124 int
          125 ui_print(char *str, int x, int y, int len) {
          126         size_t n = 0;
          127         uint32_t uni;
          128 
          129         while (*str) {
          130                 switch(*str) {
          131                 case '\t':
          132                         x += ui_expand_tab(x, y, TAB_WIDTH);
          133                         str++;
          134                         break;
          135                 default:
          136                         str += tb_utf8_char_to_unicode(&uni, str);
          137                         tb_change_cell(x, y, uni, TB_DEFAULT, TB_DEFAULT);
          138                         x++;
          139                 }
          140 
          141                 if (++n == len || x >= tb_width())
          142                         break;
          143         }
          144         return x - 1;
          145 }
          146 
          147 void
          148 ui_redraw(struct ui_s *ui) {
          149         /* redraw screen */
          150         struct line_s *tmp;
          151         int row, j;
          152         size_t s = 0;
          153 
          154         tmp = ui->top;
          155 
          156         tb_clear();
          157         tb_present();
          158 
          159         for (row = 0; row < tb_height() - 1; row++) {
          160                 if (tmp && tmp->p)
          161                         ui_print(tmp->p, 0, row, tmp->len);
          162                 else {
          163                         ui_print("~", 0, row, 1);
          164                         continue;
          165                 }
          166 
          167                 tmp = TAILQ_NEXT(tmp, entries);
          168         }
          169 
          170         tb_present();
          171 }
          172 
          173 void
          174 ui_init(struct ui_s *ui)
          175 {
          176         ui->width  = tb_width();
          177         ui->height = tb_height();
          178         ui->top    = TAILQ_FIRST(&head);
          179         ui->pos.x  = 0;
          180         ui->pos.y  = 0;
          181         ui->cur    = ui->top;
          182 }
          183 
          184 void
          185 scroll(struct ui_s *window, direction dir)
          186 {
          187         switch (dir) {
          188         case UP:
          189                 if (window->pos.y == 0) {
          190                         if (window->top == TAILQ_FIRST(&head))
          191                                 break;
          192                         window->top = TAILQ_PREV(window->top, line_s_head,
          193                                         entries);
          194                 } else {
          195                         tb_set_cursor(window->pos.x, --window->pos.y);
          196                         break;
          197                 }
          198                 window->cur = TAILQ_PREV(window->cur, line_s_head, entries);
          199                 break;
          200         case DOWN:
          201                 if (window->pos.y == tb_height() - 2) {
          202                         if (window->top == TAILQ_LAST(&head, line_s_head))
          203                                 break;
          204                         window->top = TAILQ_NEXT(window->top, entries);
          205                 } else {
          206                         if (window->cur == TAILQ_LAST(&head, line_s_head))
          207                                 break;
          208                         tb_set_cursor(window->pos.x, ++window->pos.y);
          209                         break;
          210                 }
          211                 window->cur = TAILQ_NEXT(window->cur, entries);
          212                 break;
          213         case RIGHT:
          214                 if (window->pos.x < window->cur->len - 1)
          215                         tb_set_cursor(++window->pos.x, window->pos.y);
          216                 break;
          217         case LEFT:
          218                 if (window->pos.x > 0)
          219                         tb_set_cursor(--window->pos.x, window->pos.y);
          220                 break;
          221         }
          222 }
          223 
          224 void
          225 ui_edit(char *path)
          226 {
          227         int needs_redraw;
          228         struct tb_event ev;
          229         struct ui_s window;
          230 
          231         open_file(path);
          232 
          233         ui_init(&window);
          234         tb_set_cursor(0,0);
          235         ui_redraw(&window);
          236 
          237         while (tb_poll_event(&ev)) {
          238                 switch (ev.type) {
          239                 case TB_EVENT_KEY:
          240                         /* handle keys */
          241                         switch (ev.key | ev.ch) {
          242                         case TB_KEY_CTRL_C:
          243                                 return;
          244                                 /* NOTREACHED */
          245                         case TB_KEY_CTRL_L:
          246                                 needs_redraw = 1;
          247                                 break;
          248                         case 'k':
          249                         case TB_KEY_ARROW_UP:
          250                                 scroll(&window, UP);
          251                                 needs_redraw = 1;
          252                                 break;
          253                         case 'j':
          254                         case TB_KEY_ARROW_DOWN:
          255                                 scroll(&window, DOWN);
          256                                 needs_redraw = 1;
          257                                 break;
          258                         case 'l':
          259                         case TB_KEY_ARROW_RIGHT:
          260                                 scroll(&window, RIGHT);
          261                                 needs_redraw = 1;
          262                                 break;
          263                         case 'h':
          264                         case TB_KEY_ARROW_LEFT:
          265                                 scroll(&window, LEFT);
          266                                 needs_redraw = 1;
          267                                 break;
          268                         }
          269                         break;
          270                 case TB_EVENT_RESIZE:
          271                         /* handle resizing */
          272                         needs_redraw = 1;
          273                         break;
          274                 }
          275 
          276                 if (needs_redraw) {
          277                         ui_redraw(&window);
          278                         needs_redraw = 0;
          279                 }
          280         }
          281 }
          282 
          283 
          284 int
          285 main(int argc, char **argv)
          286 {
          287         char *argv0; /* shadow */
          288 
          289         ARGBEGIN {
          290         case 'V':
          291                 /* print version */
          292                 printf("%s - %s\n", argv0, VERSION);
          293                 return 0;
          294                 break;
          295         } ARGEND
          296 
          297         init_termbox();
          298         atexit(cleanup);
          299 
          300         while (*argv)
          301                 ui_edit(*argv++);
          302 
          303         return 0;
          304 }