URI: 
       tutil.c - ltk - Socket-based GUI 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)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tutil.c (9802B)
       ---
            1 /*
            2  * Copyright (c) 2021, 2023 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 <fcntl.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 "util.h"
           29 #include "array.h"
           30 #include "memory.h"
           31 #include "txtbuf.h"
           32 
           33 /* FIXME: Should these functions really fail on memory error? */
           34 
           35 char *
           36 ltk_read_file(const char *filename, size_t *len_ret, char **errstr_ret) {
           37         long len;
           38         char *file_contents;
           39         FILE *file;
           40 
           41         /* 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 */
           42         file = fopen(filename, "r");
           43         if (!file) goto error;
           44         if (fseek(file, 0, SEEK_END)) goto errorclose;
           45         len = ftell(file);
           46         if (len < 0) goto errorclose;
           47         if (fseek(file, 0, SEEK_SET)) goto errorclose;
           48         file_contents = ltk_malloc((size_t)len + 1);
           49         clearerr(file);
           50         fread(file_contents, 1, (size_t)len, file);
           51         if (ferror(file)) goto errorclose;
           52         file_contents[len] = '\0';
           53         if (fclose(file)) goto error;
           54         *len_ret = (size_t)len;
           55         return file_contents;
           56 error:
           57         if (errstr_ret)
           58                 *errstr_ret = strerror(errno);
           59         return NULL;
           60 errorclose:
           61         if (errstr_ret)
           62                 *errstr_ret = strerror(errno);
           63         fclose(file);
           64         return NULL;
           65 }
           66 
           67 /* FIXME: not sure if errno actually is set usefully after all these functions */
           68 int
           69 ltk_write_file(const char *path, const char *data, size_t len, char **errstr_ret) {
           70         FILE *file = fopen(path, "w");
           71         if (!file) goto error;
           72         clearerr(file);
           73         if (fwrite(data, 1, len, file) < len) goto errorclose;
           74         if (fclose(file)) goto error;
           75         return 0;
           76 error:
           77         if (errstr_ret)
           78                 *errstr_ret = strerror(errno);
           79         return 1;
           80 errorclose:
           81         if (errstr_ret)
           82                 *errstr_ret = strerror(errno);
           83         fclose(file);
           84         return 1;
           85 }
           86 
           87 /* FIXME: maybe have a few standard array types defined somewhere else */
           88 LTK_ARRAY_INIT_DECL_STATIC(cmd, char *)
           89 LTK_ARRAY_INIT_IMPL_STATIC(cmd, char *)
           90 
           91 static void
           92 free_helper(char *ptr) {
           93         ltk_free(ptr);
           94 }
           95 
           96 /* FIXME: this is really ugly */
           97 /* FIXME: parse command only once in beginning instead of each time it is run? */
           98 /* FIXME: this handles double-quote, but the config parser already uses that, so
           99    it's kind of weird because it's parsed twice (also backslashes are parsed twice). */
          100 int
          101 ltk_parse_run_cmd(const char *cmdtext, size_t len, const char *filename) {
          102         int bs = 0;
          103         int in_sqstr = 0;
          104         int in_dqstr = 0;
          105         int in_ws = 1;
          106         char c;
          107         size_t cur_start = 0;
          108         int offset = 0;
          109         txtbuf *cur_arg = txtbuf_new();
          110         ltk_array(cmd) *cmd = ltk_array_create(cmd, 4);
          111         char *cmdcopy = ltk_strndup(cmdtext, len);
          112         for (size_t i = 0; i < len; i++) {
          113                 c = cmdcopy[i];
          114                 if (c == '\\') {
          115                         if (bs) {
          116                                 offset++;
          117                                 bs = 0;
          118                         } else {
          119                                 bs = 1;
          120                         }
          121                 } else if (isspace(c)) {
          122                         if (!in_sqstr && !in_dqstr) {
          123                                 if (bs) {
          124                                         if (in_ws) {
          125                                                 in_ws = 0;
          126                                                 cur_start = i;
          127                                                 offset = 0;
          128                                         } else {
          129                                                 offset++;
          130                                         }
          131                                         bs = 0;
          132                                 } else if (!in_ws) {
          133                                         /* FIXME: shouldn't this be < instead of <=? */
          134                                         if (cur_start <= i - offset)
          135                                                 txtbuf_appendn(cur_arg, cmdcopy + cur_start, i - cur_start - offset);
          136                                         /* FIXME: cmd is named horribly */
          137                                         ltk_array_append(cmd, cmd, txtbuf_get_textcopy(cur_arg));
          138                                         txtbuf_clear(cur_arg);
          139                                         in_ws = 1;
          140                                         offset = 0;
          141                                 }
          142                         /* FIXME: parsing weird here - bs just ignored */
          143                         } else if (bs) {
          144                                 bs = 0;
          145                         }
          146                 } else if (c == '%') {
          147                         if (bs) {
          148                                 if (in_ws) {
          149                                         cur_start = i;
          150                                         offset = 0;
          151                                 } else {
          152                                         offset++;
          153                                 }
          154                                 bs = 0;
          155                         } else if (!in_sqstr && filename && i < len - 1 && cmdcopy[i + 1] == 'f') {
          156                                 if (!in_ws && cur_start < i - offset)
          157                                         txtbuf_appendn(cur_arg, cmdcopy + cur_start, i - cur_start - offset);
          158                                 txtbuf_append(cur_arg, filename);
          159                                 i++;
          160                                 cur_start = i + 1;
          161                                 offset = 0;
          162                         } else if (in_ws) {
          163                                 cur_start = i;
          164                                 offset = 0;
          165                         }
          166                         in_ws = 0;
          167                 } else if (c == '"') {
          168                         if (in_sqstr) {
          169                                 bs = 0;
          170                         } else if (bs) {
          171                                 if (in_ws) {
          172                                         cur_start = i;
          173                                         offset = 0;
          174                                 } else {
          175                                         offset++;
          176                                 }
          177                                 bs = 0;
          178                         } else if (in_dqstr) {
          179                                 offset++;
          180                                 in_dqstr = 0;
          181                                 continue;
          182                         } else {
          183                                 in_dqstr = 1;
          184                                 if (in_ws) {
          185                                         cur_start = i + 1;
          186                                         offset = 0;
          187                                 } else {
          188                                         offset++;
          189                                         continue;
          190                                 }
          191                         }
          192                         in_ws = 0;
          193                 } else if (c == '\'') {
          194                         if (in_dqstr) {
          195                                 bs = 0;
          196                         } else if (bs) {
          197                                 if (in_ws) {
          198                                         cur_start = i;
          199                                         offset = 0;
          200                                 } else {
          201                                         offset++;
          202                                 }
          203                                 bs = 0;
          204                         } else if (in_sqstr) {
          205                                 offset++;
          206                                 in_sqstr = 0;
          207                                 continue;
          208                         } else {
          209                                 in_sqstr = 1;
          210                                 if (in_ws) {
          211                                         cur_start = i + 1;
          212                                         offset = 0;
          213                                 } else {
          214                                         offset++;
          215                                         continue;
          216                                 }
          217                         }
          218                         in_ws = 0;
          219                 } else if (bs) {
          220                         if (!in_sqstr && !in_dqstr) {
          221                                 if (in_ws) {
          222                                         cur_start = i;
          223                                         offset = 0;
          224                                 } else {
          225                                         offset++;
          226                                 }
          227                         }
          228                         bs = 0;
          229                         in_ws = 0;
          230                 } else {
          231                         if (in_ws) {
          232                                 cur_start = i;
          233                                 offset = 0;
          234                         }
          235                         in_ws = 0;
          236                 }
          237                 cmdcopy[i - offset] = cmdcopy[i];
          238         }
          239         if (in_sqstr || in_dqstr) {
          240                 ltk_warn("Unterminated string in command\n");
          241                 goto error;
          242         }
          243         if (!in_ws) {
          244                 if (cur_start <= len - offset)
          245                         txtbuf_appendn(cur_arg, cmdcopy + cur_start, len - cur_start - offset);
          246                 ltk_array_append(cmd, cmd, txtbuf_get_textcopy(cur_arg));
          247         }
          248         if (cmd->len == 0) {
          249                 ltk_warn("Empty command\n");
          250                 goto error;
          251         }
          252         ltk_array_append(cmd, cmd, NULL); /* necessary for execvp */
          253         int fret = -1;
          254         if ((fret = fork()) < 0) {
          255                 ltk_warn("Unable to fork\n");
          256                 goto error;
          257         } else if (fret == 0) {
          258                 if (execvp(cmd->buf[0], cmd->buf) == -1) {
          259                         /* FIXME: what to do on error here? */
          260                         exit(1);
          261                 }
          262         } else {
          263                 ltk_free(cmdcopy);
          264                 txtbuf_destroy(cur_arg);
          265                 ltk_array_destroy_deep(cmd, cmd, &free_helper);
          266                 return fret;
          267         }
          268 error:
          269         ltk_free(cmdcopy);
          270         txtbuf_destroy(cur_arg);
          271         ltk_array_destroy_deep(cmd, cmd, &free_helper);
          272         return -1;
          273 }
          274 
          275 /* If `needed` is larger than `*alloc_size`, resize `*str` to
          276    `max(needed, *alloc_size * 2)`. Aborts program on error. */
          277 void
          278 ltk_grow_string(char **str, int *alloc_size, int needed) {
          279         if (needed <= *alloc_size) return;
          280         int new_size = needed > (*alloc_size * 2) ? needed : (*alloc_size * 2);
          281         char *new = ltk_realloc(*str, new_size);
          282         *str = new;
          283         *alloc_size = new_size;
          284 }
          285 
          286 /* Get the directory to store ltk files in and create it if it doesn't exist yet.
          287    This first checks the environment variable LTKDIR and, if that doesn't
          288    exist, the home directory with "/.ltk" appended.
          289    Returns NULL on error. */
          290 char *
          291 ltk_setup_directory(void) {
          292         char *dir, *dir_orig;
          293         struct passwd *pw;
          294         uid_t uid;
          295         int len;
          296 
          297         dir_orig = getenv("LTKDIR");
          298         if (dir_orig) {
          299                 dir = ltk_strdup(dir_orig);
          300                 /*
          301                 if (!dir)
          302                         return NULL;
          303                 */
          304         } else {
          305                 uid = getuid();
          306                 pw = getpwuid(uid);
          307                 if (!pw)
          308                         return NULL;
          309                 len = strlen(pw->pw_dir);
          310                 dir = ltk_malloc(len + 6);
          311                 /*
          312                 if (!dir)
          313                         return NULL;
          314                 */
          315                 strcpy(dir, pw->pw_dir);
          316                 strcpy(dir + len, "/.ltk");
          317         }
          318 
          319         if (mkdir(dir, 0770) < 0) {
          320                 if (errno != EEXIST)
          321                         return NULL;
          322         }
          323 
          324         return dir;
          325 }
          326 
          327 /* Concatenate the two given strings and return the result.
          328    This allocates new memory for the result string, unlike
          329    the actual strcat. Aborts program on error */
          330 char *
          331 ltk_strcat_useful(const char *str1, const char *str2) {
          332         int len1, len2;
          333         char *ret;
          334 
          335         len1 = strlen(str1);
          336         len2 = strlen(str2);
          337         ret = ltk_malloc(len1 + len2 + 1);
          338         strcpy(ret, str1);
          339         strcpy(ret + len1, str2);
          340 
          341         return ret;
          342 }
          343 
          344 void
          345 ltk_warn(const char *format, ...) {
          346         va_list args;
          347         va_start(args, format);
          348         ltk_log_msg("Warning", format, args);
          349         va_end(args);
          350 }
          351 
          352 void
          353 ltk_fatal(const char *format, ...) {
          354         va_list args;
          355         va_start(args, format);
          356         ltk_log_msg("Fatal", format, args);
          357         va_end(args);
          358         ltk_cleanup();
          359 
          360         exit(1);
          361 }
          362 
          363 void
          364 ltk_warn_errno(const char *format, ...) {
          365         va_list args;
          366         char *errstr = strerror(errno);
          367         va_start(args, format);
          368         ltk_log_msg("Warning", format, args);
          369         va_end(args);
          370         ltk_warn("system error: %s\n", errstr);
          371 }
          372 
          373 void
          374 ltk_fatal_errno(const char *format, ...) {
          375         va_list args;
          376         char *errstr = strerror(errno);
          377         va_start(args, format);
          378         ltk_log_msg("Fatal", format, args);
          379         va_end(args);
          380         ltk_fatal("system error: %s\n", errstr);
          381 }
          382 
          383 int
          384 str_array_equal(const char *terminated, const char *array, size_t len) {
          385         if (!strncmp(terminated, array, len)) {
          386                 /* this is kind of inefficient, but there's no way to know
          387                    otherwise if strncmp just stopped comparing after a '\0' */
          388                 return strlen(terminated) == len;
          389         }
          390         return 0;
          391 }
          392 
          393 size_t
          394 prev_utf8(char *text, size_t index) {
          395         if (index == 0)
          396                 return 0;
          397         size_t i = index - 1;
          398         /* find valid utf8 char - this probably needs to be improved */
          399         while (i > 0 && ((text[i] & 0xC0) == 0x80))
          400                 i--;
          401         return i;
          402 }
          403 
          404 size_t
          405 next_utf8(char *text, size_t len, size_t index) {
          406         if (index >= len)
          407                 return len;
          408         size_t i = index + 1;
          409         while (i < len && ((text[i] & 0xC0) == 0x80))
          410                 i++;
          411         return i;
          412 }
          413 
          414 int
          415 set_nonblock(int fd) {
          416         int flags = fcntl(fd, F_GETFL, 0);
          417         if (flags == -1)
          418                 return -1;
          419         if (fcntl(fd, F_SETFL, flags | O_NONBLOCK))
          420                 return -1;
          421         return 0;
          422 }