t* sacc + cursorline and uri preview
       
   URI git clone git://git.codevoid.de/sacc-sdk
   DIR Log
   DIR Files
   DIR Refs
   DIR LICENSE
       ---
   DIR commit fdae99f75651ec33854a55654887608fb4f2947c
   DIR parent 4dff6dde00bd2e8062f678d6ca2207d00119474f
   URI Author: Quentin Rameau <quinq@fifth.space>
       Date:   Fri,  7 Jul 2017 15:16:30 +0200
       
       Handle (hopefuly) all invalid format streams
       
       Diffstat:
         M sacc.c                              |     112 +++++++++++++++----------------
         M ui_ti.c                             |      37 ++++++++++++++++---------------
         M ui_txt.c                            |      23 ++++++++++-------------
       
       3 files changed, 84 insertions(+), 88 deletions(-)
       ---
   DIR diff --git a/sacc.c b/sacc.c
       t@@ -57,6 +57,17 @@ xmalloc(const size_t n)
                return m;
        }
        
       +static void *
       +xcalloc(size_t n)
       +{
       +        char *m = xmalloc(n);
       +
       +        while (n)
       +                m[--n] = 0;
       +
       +        return m;
       +}
       +
        static char *
        xstrdup(const char *str)
        {
       t@@ -147,20 +158,8 @@ pickfield(char **raw, char sep)
        {
                char *c, *f = *raw;
        
       -        for (c = *raw; *c != sep; ++c) {
       -                switch (*c) {
       -                case '\t':
       -                        if (sep == '\r')
       -                                *c = '\0';
       -                case '\n':
       -                case '\r':
       -                case '\0':
       -                        return NULL;
       -                default:
       -                        continue;
       -                }
       -                break;
       -        }
       +        for (c = *raw; *c && *c != sep; ++c)
       +                ;
        
                *c = '\0';
                *raw = c+1;
       t@@ -168,35 +167,47 @@ pickfield(char **raw, char sep)
                return f;
        }
        
       +static char *
       +invaliditem(char *raw)
       +{
       +        char c;
       +        int tabs;
       +
       +        for (tabs = 0; (c = *raw) && c != '\n'; ++raw) {
       +                if (c == '\t')
       +                        ++tabs;
       +        }
       +        if (c)
       +                *raw++ = '\0';
       +
       +        return (tabs == 3) ? NULL : raw;
       +}
       +
        static Item *
        molditem(char **raw)
        {
                Item *item;
       -        char type, *username, *selector, *host, *port;
       +        char *next;
        
                if (!*raw)
                        return NULL;
        
       -        if (!(type = *raw[0]++) ||
       -            !(username = pickfield(raw, '\t')) ||
       -            !(selector = pickfield(raw, '\t')) ||
       -            !(host = pickfield(raw, '\t')) ||
       -            !(port = pickfield(raw, '\r')) ||
       -            *raw[0]++ != '\n')
       -                return NULL;
       +        item = xcalloc(sizeof(Item));
        
       -        item = xmalloc(sizeof(Item));
       +        if ((next = invaliditem(*raw))) {
       +                item->type = 'i';
       +                item->username = *raw;
       +                *raw = next;
       +                return item;
       +        }
        
       -        item->type = type;
       -        item->username = username;
       -        item->selector = selector;
       -        item->host = host;
       -        item->port = port;
       -        item->raw = NULL;
       -        item->dir = NULL;
       -        item->entry = NULL;
       -        item->printoff = 0;
       -        item->curline = 0;
       +        item->type = *raw[0]++;
       +        item->username = pickfield(raw, '\t');
       +        item->selector = pickfield(raw, '\t');
       +        item->host = pickfield(raw, '\t');
       +        item->port = pickfield(raw, '\r');
       +        if (!*raw[0])
       +                ++*raw;
        
                return item;
        }
       t@@ -204,38 +215,25 @@ molditem(char **raw)
        static Dir *
        molddiritem(char *raw)
        {
       -        Item *item, **items = NULL;
       -        char *crlf, *p;
       +        Item **items = NULL;
       +        char *s, *nl, *p;
                Dir *dir;
                size_t i, nitems;
        
       -        for (crlf = raw, nitems = 0; p = strstr(crlf, "\r\n"); ++nitems)
       -                crlf = p+2;
       -        if (nitems <= 1)
       -                return NULL;
       -        if (!strcmp(crlf-3, ".\r\n"))
       +        for (s = nl = raw, nitems = 0; p = strchr(nl, '\n'); ++nitems) {
       +                s = nl;
       +                nl = p+1;
       +        }
       +        if (!strcmp(s, ".\r\n") || !strcmp(s, ".\n"))
                        --nitems;
       -        else
       -                fprintf(stderr, "Parsing error: missing .\\r\\n last line\n");
       +        if (!nitems)
       +                return NULL;
        
                dir = xmalloc(sizeof(Dir));
                items = xreallocarray(items, nitems, sizeof(Item*));
        
       -        for (i = 0; i < nitems; ++i) {
       -                if (item = molditem(&raw)) {
       -                        items[i] = item;
       -                } else {
       -                        fprintf(stderr, "Parsing error: dir entity: %d\n", i+1);
       -                        items = xreallocarray(items, i, sizeof(Item*));
       -                        nitems = i;
       -                        break;
       -                }
       -        }
       -
       -        if (!items) {
       -                free(dir);
       -                return NULL;
       -        }
       +        for (i = 0; i < nitems; ++i)
       +                items[i] = molditem(&raw);
        
                dir->items = items;
                dir->nitems = nitems;
   DIR diff --git a/ui_ti.c b/ui_ti.c
       t@@ -57,7 +57,7 @@ help(void)
        static void
        displaystatus(Item *item)
        {
       -        size_t nitems = item->dir->nitems;
       +        size_t nitems = item->dir ? item->dir->nitems : 0;
        
                putp(tparm(save_cursor));
        
       t@@ -75,13 +75,18 @@ displaystatus(Item *item)
        void
        display(Item *entry)
        {
       -        Item *item, **items;
       +        Item **items;
                size_t i, curln, lastln, nitems, printoff;
        
                if (entry->type != '1')
                        return;
        
                putp(tparm(clear_screen));
       +        displaystatus(entry);
       +
       +        if (!entry->dir)
       +                return;
       +
                putp(tparm(save_cursor));
        
                items = entry->dir->items;
       t@@ -91,22 +96,19 @@ display(Item *entry)
                lastln = printoff + lines-1; /* one off for status bar */
        
                for (i = printoff; i < nitems && i < lastln; ++i) {
       -                if (item = items[i]) {
       -                        if (i != printoff)
       -                                putp(tparm(cursor_down));
       -                        if (i == curln) {
       -                                putp(tparm(save_cursor));
       -                                putp(tparm(enter_standout_mode));
       -                        }
       -                        printitem(item);
       -                        putp(tparm(column_address, 0));
       -                        if (i == curln)
       -                                putp(tparm(exit_standout_mode));
       +                if (i != printoff)
       +                        putp(tparm(cursor_down));
       +                if (i == curln) {
       +                        putp(tparm(save_cursor));
       +                        putp(tparm(enter_standout_mode));
                        }
       +                printitem(items[i]);
       +                putp(tparm(column_address, 0));
       +                if (i == curln)
       +                        putp(tparm(exit_standout_mode));
                }
        
                putp(tparm(restore_cursor));
       -        displaystatus(entry);
                fflush(stdout);
        }
        
       t@@ -166,8 +168,7 @@ movecurline(Item *item, int l)
        Item *
        selectitem(Item *entry)
        {
       -        Item *hole;
       -        int item, nitems;
       +        Dir *dir = entry->dir;
        
                for (;;) {
                        switch (getchar()) {
       t@@ -199,8 +200,8 @@ selectitem(Item *entry)
                        case _key_pgnext:
                        case '\r':
                        pgnext:
       -                        if (entry->dir->items[entry->curline]->type < '2')
       -                                return entry->dir->items[entry->curline];
       +                        if (dir)
       +                                return dir->items[entry->curline];
                                continue;
                        case _key_lndown:
                        lndown:
   DIR diff --git a/ui_txt.c b/ui_txt.c
       t@@ -54,7 +54,7 @@ ndigits(size_t n)
        static void
        printstatus(Item *item)
        {
       -        size_t nitems = item->dir->nitems;
       +        size_t nitems = item->dir ? item->dir->nitems : 0;
        
                printf("%3lld%%%*c %s:%s%s (h for help): ",
                       (item->printoff + lines >= nitems) ? 100 :
       t@@ -65,11 +65,11 @@ printstatus(Item *item)
        void
        display(Item *entry)
        {
       -        Item *item, **items;
       +        Item **items;
                size_t i, lines, nitems;
                int nd;
        
       -        if (entry->type != '1')
       +        if (entry->type != '1' || !entry->dir)
                        return;
        
                items = entry->dir->items;
       t@@ -78,15 +78,12 @@ display(Item *entry)
                nd = ndigits(nitems);
        
                for (i = entry->printoff; i < nitems && i < lines; ++i) {
       -                if (item = items[i]) {
       -                        printf("%*zu %-4s%c %s\n", nd, i+1,
       -                               item->type != 'i' ?
       -                               typedisplay(item->type) : "",
       -                               item->type > '1' ? '|' : '+',
       -                               items[i]->username);
       -                } else {
       -                        printf("%*zu  !! |\n", nd, i+1);
       -                }
       +                item = items[i];
       +                printf("%*zu %-4s%c %s\n", nd, i+1,
       +                       item->type != 'i' ?
       +                       typedisplay(item->type) : "",
       +                       item->type > '1' ? '|' : '+',
       +                       item->username);
                }
        
                fflush(stdout);
       t@@ -113,7 +110,7 @@ selectitem(Item *entry)
        
                        if (!strcmp(buf, "n\n")) {
                                lines = termlines();
       -                        if (lines < entry->dir->nitems - entry->printoff &&
       +                        if (lines < nitems - entry->printoff &&
                                    lines < (size_t)-1 - entry->printoff)
                                        entry->printoff += lines;
                                return entry;