URI:
       util.c - ltk - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       util.c (5609B)
       ---
            1 /*
            2  * Copyright (c) 2021-2024 lumidify <nobody@lumidify.org>
            3  *
            4  * Permission to use, copy, modify, and/or distribute this software for any
            5  * purpose with or without fee is hereby granted, provided that the above
            6  * copyright notice and this permission notice appear in all copies.
            7  *
            8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           15  */
           16 
           17 #include <pwd.h>
           18 #include <time.h>
           19 #include <ctype.h>
           20 #include <errno.h>
           21 #include <stdio.h>
           22 #include <stdlib.h>
           23 #include <string.h>
           24 #include <stdarg.h>
           25 #include <unistd.h>
           26 #include <sys/stat.h>
           27 
           28 #include "ltk.h"
           29 #include "util.h"
           30 #include "array.h"
           31 #include "memory.h"
           32 #include "txtbuf.h"
           33 
           34 /* FIXME: Should these functions really fail on memory error? */
           35 
           36 char *
           37 ltk_read_file(const char *filename, size_t *len_ret, char **errstr_ret) {
           38         long len;
           39         char *file_contents;
           40         FILE *file;
           41 
           42         /* FIXME: https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file */
           43         file = fopen(filename, "r");
           44         if (!file) goto error;
           45         if (fseek(file, 0, SEEK_END)) goto errorclose;
           46         len = ftell(file);
           47         if (len < 0) goto errorclose;
           48         if (fseek(file, 0, SEEK_SET)) goto errorclose;
           49         file_contents = ltk_malloc((size_t)len + 1);
           50         clearerr(file);
           51         fread(file_contents, 1, (size_t)len, file);
           52         if (ferror(file)) goto errorclose;
           53         file_contents[len] = '\0';
           54         if (fclose(file)) goto error;
           55         *len_ret = (size_t)len;
           56         return file_contents;
           57 error:
           58         if (errstr_ret)
           59                 *errstr_ret = strerror(errno);
           60         return NULL;
           61 errorclose:
           62         if (errstr_ret)
           63                 *errstr_ret = strerror(errno);
           64         fclose(file);
           65         return NULL;
           66 }
           67 
           68 /* FIXME: not sure if errno actually is set usefully after all these functions */
           69 int
           70 ltk_write_file(const char *path, const char *data, size_t len, char **errstr_ret) {
           71         FILE *file = fopen(path, "w");
           72         if (!file) goto error;
           73         clearerr(file);
           74         if (fwrite(data, 1, len, file) < len) goto errorclose;
           75         if (fclose(file)) goto error;
           76         return 0;
           77 error:
           78         if (errstr_ret)
           79                 *errstr_ret = strerror(errno);
           80         return 1;
           81 errorclose:
           82         if (errstr_ret)
           83                 *errstr_ret = strerror(errno);
           84         fclose(file);
           85         return 1;
           86 }
           87 
           88 /* If `needed` is larger than `*alloc_size`, resize `*str` to
           89    `max(needed, *alloc_size * 2)`. Aborts program on error. */
           90 void
           91 ltk_grow_string(char **str, int *alloc_size, int needed) {
           92         if (needed <= *alloc_size) return;
           93         int new_size = needed > (*alloc_size * 2) ? needed : (*alloc_size * 2);
           94         char *new = ltk_realloc(*str, new_size);
           95         *str = new;
           96         *alloc_size = new_size;
           97 }
           98 
           99 /* Get the directory to store ltk files in and optionally create it if it
          100    doesn't exist yet and `create` is set.
          101    This first checks the environment variable `env` and, if that doesn't
          102    exist, the home directory with "/" and `default` appended.
          103    Returns NULL on error. */
          104 char *
          105 ltk_setup_directory(const char *envname, const char *defaultname, int create) {
          106         char *dir, *dir_orig;
          107         struct passwd *pw;
          108         uid_t uid;
          109 
          110         dir_orig = getenv(envname);
          111         if (dir_orig) {
          112                 dir = ltk_strdup(dir_orig);
          113         } else {
          114                 uid = getuid();
          115                 pw = getpwuid(uid);
          116                 if (!pw)
          117                         return NULL;
          118                 size_t len = strlen(pw->pw_dir);
          119                 size_t dlen = strlen(defaultname);
          120                 dir = ltk_malloc(len + dlen + 2);
          121                 strcpy(dir, pw->pw_dir);
          122                 dir[len] = '/';
          123                 strcpy(dir + len + 1, defaultname);
          124         }
          125 
          126         if (create && mkdir(dir, 0700) < 0) {
          127                 if (errno != EEXIST)
          128                         return NULL;
          129         }
          130 
          131         return dir;
          132 }
          133 
          134 /* Concatenate the two given strings and return the result.
          135    This allocates new memory for the result string, unlike
          136    the actual strcat. Aborts program on error */
          137 char *
          138 ltk_strcat_useful(const char *str1, const char *str2) {
          139         int len1, len2;
          140         char *ret;
          141 
          142         len1 = strlen(str1);
          143         len2 = strlen(str2);
          144         ret = ltk_malloc(len1 + len2 + 1);
          145         strcpy(ret, str1);
          146         strcpy(ret + len1, str2);
          147 
          148         return ret;
          149 }
          150 
          151 static void
          152 ltk_log_msg(const char *mode, const char *format, va_list args) {
          153         char logtime[25]; /* FIXME: This should always be big enough, right? */
          154         time_t clock;
          155         struct tm *timeptr;
          156 
          157         time(&clock);
          158         timeptr = localtime(&clock);
          159         strftime(logtime, 25, "%Y-%m-%d %H:%M:%S", timeptr);
          160 
          161         fprintf(stderr, "%s ltk %s: ", logtime, mode);
          162         vfprintf(stderr, format, args);
          163 }
          164 
          165 LTK_GEN_LOG_FUNCS(ltk, ltk_log_msg, ltk_deinit)
          166 
          167 int
          168 str_array_equal(const char *terminated, const char *array, size_t len) {
          169         if (!strncmp(terminated, array, len)) {
          170                 /* this is kind of inefficient, but there's no way to know
          171                    otherwise if strncmp just stopped comparing after a '\0' */
          172                 return strlen(terminated) == len;
          173         }
          174         return 0;
          175 }
          176 
          177 size_t
          178 prev_utf8(char *text, size_t index) {
          179         if (index == 0)
          180                 return 0;
          181         size_t i = index - 1;
          182         /* find valid utf8 char - this probably needs to be improved */
          183         while (i > 0 && ((text[i] & 0xC0) == 0x80))
          184                 i--;
          185         return i;
          186 }
          187 
          188 size_t
          189 next_utf8(char *text, size_t len, size_t index) {
          190         if (index >= len)
          191                 return len;
          192         size_t i = index + 1;
          193         while (i < len && ((text[i] & 0xC0) == 0x80))
          194                 i++;
          195         return i;
          196 }
          197 
          198 void
          199 ltk_assert_impl(const char *file, int line, const char *func, const char *failedexpr)
          200 {
          201         (void)fprintf(stderr,
          202             "assertion \"%s\" failed: file \"%s\", line %d, function \"%s\"\n",
          203             failedexpr, file, line, func);
          204         abort();
          205         /* NOTREACHED */
          206 }