share/gui: don't use less of widget width for truncation than available
[neverball] / share / common.c
index d2d1f1b..3818202 100644 (file)
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <errno.h>
 #include <time.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <assert.h>
 
 #include "common.h"
+#include "fs.h"
 
 #define MAXSTR 256
 
 /*---------------------------------------------------------------------------*/
 
-int read_line(char **dst, FILE *fin)
+int read_line(char **dst, fs_file fin)
 {
-    char buffer[MAXSTR] = "";
-    int  buffer_size    = 0;
+    char buff[MAXSTR];
 
-    char *store      = NULL;
-    char *store_new  = NULL;
-    int   store_size = 0;
+    char *line, *new;
+    size_t len0, len1;
 
-    int seen_newline = 0;
+    line = NULL;
 
-    while (!seen_newline)
+    while (fs_gets(buff, sizeof (buff), fin))
     {
-        if (fgets(buffer, sizeof (buffer), fin) == NULL)
-        {
-            if (store_size > 0)
-                break;
-            else
-            {
-                *dst = NULL;
-                return 0;
-            }
-        }
-
-        buffer_size = strlen(buffer) + 1;
+        /* Append to data read so far. */
 
-        /* Erase trailing newline. */
-
-        if (buffer[buffer_size - 2] == '\n')
+        if (line)
         {
-            seen_newline = 1;
-            buffer[buffer_size - 2] = '\0';
-            buffer_size--;
+            new  = concat_string(line, buff, NULL);
+            free(line);
+            line = new;
         }
-
-        /* Allocate or reallocate space for the buffer. */
-
-        if ((store_new = (char *) realloc(store, store_size + buffer_size)))
+        else
         {
-            /* Avoid passing garbage to string functions. */
+            line = strdup(buff);
+        }
 
-            if (store == NULL)
-                store_new[0] = '\0';
+        /* Strip newline, if any. */
 
-            store       = store_new;
-            store_size += buffer_size;
+        len0 = strlen(line);
+        strip_newline(line);
+        len1 = strlen(line);
 
-            store_new = NULL;
-        }
-        else
+        if (len1 != len0)
         {
-            fprintf(stderr, "Failed to allocate memory.\n");
-
-            free(store);
-            *dst = NULL;
-            return 0;
+            /* We hit a newline, clean up and break. */
+            line = realloc(line, len1 + 1);
+            break;
         }
-
-        strncat(store, buffer, buffer_size);
     }
 
-    *dst = store;
-
-    return 1;
+    return (*dst = line) ? 1 : 0;
 }
 
 char *strip_newline(char *str)
@@ -105,7 +83,7 @@ char *strip_newline(char *str)
     return str;
 }
 
-char *strdup(const char *src)
+char *dupe_string(const char *src)
 {
     char *dst = NULL;
 
@@ -115,6 +93,52 @@ char *strdup(const char *src)
     return dst;
 }
 
+char *concat_string(const char *first, ...)
+{
+    char *full;
+
+    if ((full = strdup(first)))
+    {
+        const char *part;
+        va_list ap;
+
+        va_start(ap, first);
+
+        while ((part = va_arg(ap, const char *)))
+        {
+            char *new;
+
+            if ((new = realloc(full, strlen(full) + strlen(part) + 1)))
+            {
+                full = new;
+                strcat(full, part);
+            }
+            else
+            {
+                free(full);
+                full = NULL;
+                break;
+            }
+        }
+
+        va_end(ap);
+    }
+
+    return full;
+}
+
+char *trunc_string(const char *src, char *dst, int len)
+{
+    static const char ell[] = "...";
+
+    assert(len > sizeof (ell));
+
+    if (dst[len - 1] = '\0', strncpy(dst, src, len), dst[len - 1] != '\0')
+        strcpy(dst + len - sizeof (ell), ell);
+
+    return dst;
+}
+
 time_t make_time_from_utc(struct tm *tm)
 {
     struct tm local, *utc;
@@ -172,31 +196,66 @@ void file_copy(FILE *fin, FILE *fout)
         fwrite(buff, 1, size, fout);
 }
 
-char *base_name(const char *name, const char *suffix)
+/*---------------------------------------------------------------------------*/
+
+int path_is_sep(int c)
 {
-    static char buf[MAXSTR];
-    char *base;
+#ifdef _WIN32
+    return c == '/' || c == '\\';
+#else
+    return c == '/';
+#endif
+}
 
-    if (!name)
-        return NULL;
+int path_is_abs(const char *path)
+{
+    if (path_is_sep(path[0]))
+        return 1;
 
-    /* Remove the directory part. */
+#ifdef _WIN32
+    if (isalpha(path[0]) && path[1] == ':' && path_is_sep(path[2]))
+        return 1;
+#endif
 
-    base = strrchr(name, '/');
+    return 0;
+}
+
+static char *path_last_sep(const char *path)
+{
+    char *sep;
+
+    sep = strrchr(path, '/');
 
 #ifdef _WIN32
-    if (!base)
-        base = strrchr(name, '\\');
+    if (!sep)
+    {
+        sep = strrchr(path, '\\');
+    }
     else
     {
         char *tmp;
 
-        if ((tmp = strrchr(base, '\\')))
-            base = tmp;
+        if ((tmp = strrchr(sep, '\\')))
+            sep = tmp;
     }
 #endif
 
-    strncpy(buf, base ? base + 1 : name, sizeof (buf));
+    return sep;
+}
+
+char *base_name(const char *name, const char *suffix)
+{
+    static char buf[MAXSTR];
+    char *base;
+
+    if (!name)
+        return NULL;
+
+    /* Remove the directory part. */
+
+    base = path_last_sep(name);
+
+    strncpy(buf, base ? base + 1 : name, sizeof (buf) - 1);
 
     /* Remove the suffix. */
 
@@ -211,4 +270,54 @@ char *base_name(const char *name, const char *suffix)
     return buf;
 }
 
+const char *dir_name(const char *name)
+{
+    static char buff[MAXSTR];
+
+    char *sep;
+
+    strncpy(buff, name, sizeof (buff) - 1);
+
+    if ((sep = path_last_sep(buff)))
+    {
+        if (sep == buff)
+            return "/";
+
+        *sep = '\0';
+
+        return buff;
+    }
+
+    return ".";
+}
+
+/*
+ * Given a path to a file REF and another path REL relative to REF,
+ * construct and return a new path that can be used to refer to REL
+ * directly.
+ */
+char *path_resolve(const char *ref, const char *rel)
+{
+    static char new[MAXSTR * 2];
+
+    if (path_is_abs(rel))
+    {
+        strncpy(new, rel, sizeof (new) - 1);
+        return new;
+    }
+
+    strncpy(new, dir_name(ref), sizeof (new) - 1);
+    strncat(new, "/",           sizeof (new) - strlen(new) - 1);
+    strncat(new, rel,           sizeof (new) - strlen(new) - 1);
+
+    return new;
+}
+
+/*---------------------------------------------------------------------------*/
+
+int rand_between(int low, int high)
+{
+    return low + rand() / (RAND_MAX / (high - low + 1) + 1);
+}
+
 /*---------------------------------------------------------------------------*/