A Simple X Image Viewer
 
 
 
 
 
 

345 lines
6.7 KiB

  1. /* sxiv: util.c
  2. * Copyright (c) 2011 Bert Muennich <muennich at informatik.hu-berlin.de>
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms of the GNU General Public License as published by the
  6. * Free Software Foundation; either version 2 of the License, or (at your
  7. * option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. */
  18. #define _POSIX_C_SOURCE 200112L
  19. #include <stdlib.h>
  20. #include <string.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <unistd.h>
  24. #include <errno.h>
  25. #include "options.h"
  26. #include "util.h"
  27. enum {
  28. BUF_SIZE = 1024,
  29. DNAME_CNT = 512,
  30. FNAME_LEN = 1024
  31. };
  32. void cleanup();
  33. void* s_malloc(size_t size) {
  34. void *ptr;
  35. if (!(ptr = malloc(size)))
  36. die("could not allocate memory");
  37. return ptr;
  38. }
  39. void* s_realloc(void *ptr, size_t size) {
  40. if (!(ptr = realloc(ptr, size)))
  41. die("could not allocate memory");
  42. return ptr;
  43. }
  44. char* s_strdup(char *s) {
  45. char *d = NULL;
  46. if (s) {
  47. if (!(d = malloc(strlen(s) + 1)))
  48. die("could not allocate memory");
  49. strcpy(d, s);
  50. }
  51. return d;
  52. }
  53. void warn(const char* fmt, ...) {
  54. va_list args;
  55. if (!fmt || options->quiet)
  56. return;
  57. va_start(args, fmt);
  58. fprintf(stderr, "sxiv: warning: ");
  59. vfprintf(stderr, fmt, args);
  60. fprintf(stderr, "\n");
  61. va_end(args);
  62. }
  63. void die(const char* fmt, ...) {
  64. va_list args;
  65. if (!fmt)
  66. return;
  67. va_start(args, fmt);
  68. fprintf(stderr, "sxiv: error: ");
  69. vfprintf(stderr, fmt, args);
  70. fprintf(stderr, "\n");
  71. va_end(args);
  72. cleanup();
  73. exit(1);
  74. }
  75. ssize_t get_line(char **buf, size_t *n, FILE *stream) {
  76. size_t len;
  77. char *s;
  78. if (!stream || feof(stream) || ferror(stream))
  79. return -1;
  80. if (!*buf || !*n) {
  81. *n = BUF_SIZE;
  82. *buf = (char*) s_malloc(*n);
  83. }
  84. s = *buf;
  85. while (1) {
  86. if (!fgets(s, *n - (s - *buf), stream))
  87. return -1;
  88. len = strlen(s);
  89. if (feof(stream))
  90. break;
  91. if (len > 0 && s[len-1] == '\n')
  92. break;
  93. if (len + 1 == *n - (s - *buf)) {
  94. *buf = (char*) s_realloc(*buf, 2 * *n);
  95. s = *buf + *n - 1;
  96. *n *= 2;
  97. } else {
  98. s += len;
  99. }
  100. }
  101. return s - *buf + len;
  102. }
  103. void size_readable(float *size, const char **unit) {
  104. const char *units[] = { "", "K", "M", "G" };
  105. int i;
  106. for (i = 0; i < ARRLEN(units) && *size > 1024; i++)
  107. *size /= 1024;
  108. *unit = units[MIN(i, ARRLEN(units) - 1)];
  109. }
  110. char* absolute_path(const char *filename) {
  111. size_t len;
  112. const char *basename;
  113. char *dir, *dirname = NULL, *path = NULL, *s;
  114. char *cwd = NULL, *twd = NULL;
  115. if (!filename || *filename == '\0' || *filename == '/')
  116. return NULL;
  117. len = FNAME_LEN;
  118. cwd = (char*) s_malloc(len);
  119. while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
  120. len *= 2;
  121. cwd = (char*) s_realloc(cwd, len);
  122. }
  123. if (!s)
  124. goto error;
  125. s = strrchr(filename, '/');
  126. if (s) {
  127. len = s - filename;
  128. dirname = (char*) s_malloc(len + 1);
  129. strncpy(dirname, filename, len);
  130. dirname[len] = '\0';
  131. basename = s + 1;
  132. if (chdir(cwd))
  133. /* we're not able to come back afterwards */
  134. goto error;
  135. if (chdir(dirname))
  136. goto error;
  137. len = FNAME_LEN;
  138. twd = (char*) s_malloc(len);
  139. while (!(s = getcwd(twd, len)) && errno == ERANGE) {
  140. len *= 2;
  141. twd = (char*) s_realloc(twd, len);
  142. }
  143. if (chdir(cwd))
  144. die("could not revert to prior working directory");
  145. if (!s)
  146. goto error;
  147. dir = twd;
  148. } else {
  149. /* only a single filename given */
  150. basename = filename;
  151. dir = cwd;
  152. }
  153. len = strlen(dir) + strlen(basename) + 2;
  154. path = (char*) s_malloc(len);
  155. snprintf(path, len, "%s/%s", dir, basename);
  156. goto end;
  157. error:
  158. if (path) {
  159. free(path);
  160. path = NULL;
  161. }
  162. end:
  163. if (dirname)
  164. free(dirname);
  165. if (cwd)
  166. free(cwd);
  167. if (twd)
  168. free(twd);
  169. return path;
  170. }
  171. int r_opendir(r_dir_t *rdir, const char *dirname) {
  172. if (!rdir || !dirname || !*dirname)
  173. return -1;
  174. if (!(rdir->dir = opendir(dirname))) {
  175. rdir->name = NULL;
  176. rdir->stack = NULL;
  177. return -1;
  178. }
  179. rdir->stcap = DNAME_CNT;
  180. rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
  181. rdir->stlen = 0;
  182. rdir->name = (char*) dirname;
  183. rdir->d = 0;
  184. return 0;
  185. }
  186. int r_closedir(r_dir_t *rdir) {
  187. int ret = 0;
  188. if (!rdir)
  189. return -1;
  190. if (rdir->stack) {
  191. while (rdir->stlen > 0)
  192. free(rdir->stack[--rdir->stlen]);
  193. free(rdir->stack);
  194. rdir->stack = NULL;
  195. }
  196. if (rdir->dir) {
  197. if (!(ret = closedir(rdir->dir)))
  198. rdir->dir = NULL;
  199. }
  200. if (rdir->d && rdir->name) {
  201. free(rdir->name);
  202. rdir->name = NULL;
  203. }
  204. return ret;
  205. }
  206. char* r_readdir(r_dir_t *rdir) {
  207. size_t len;
  208. char *filename;
  209. struct dirent *dentry;
  210. struct stat fstats;
  211. if (!rdir || !rdir->dir || !rdir->name)
  212. return NULL;
  213. while (1) {
  214. if (rdir->dir && (dentry = readdir(rdir->dir))) {
  215. if (!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, ".."))
  216. continue;
  217. len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
  218. filename = (char*) s_malloc(len);
  219. snprintf(filename, len, "%s%s%s", rdir->name,
  220. rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
  221. dentry->d_name);
  222. if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
  223. /* put subdirectory on the stack */
  224. if (rdir->stlen == rdir->stcap) {
  225. rdir->stcap *= 2;
  226. rdir->stack = (char**) s_realloc(rdir->stack,
  227. rdir->stcap * sizeof(char*));
  228. }
  229. rdir->stack[rdir->stlen++] = filename;
  230. continue;
  231. }
  232. return filename;
  233. }
  234. if (rdir->stlen > 0) {
  235. /* open next subdirectory */
  236. closedir(rdir->dir);
  237. if (rdir->d)
  238. free(rdir->name);
  239. rdir->name = rdir->stack[--rdir->stlen];
  240. rdir->d = 1;
  241. if (!(rdir->dir = opendir(rdir->name)))
  242. warn("could not open directory: %s", rdir->name);
  243. continue;
  244. }
  245. /* no more entries */
  246. break;
  247. }
  248. return NULL;
  249. }
  250. int r_mkdir(const char *path) {
  251. char *dir, *d;
  252. struct stat stats;
  253. int err = 0;
  254. if (!path || !*path)
  255. return -1;
  256. if (!stat(path, &stats)) {
  257. if (S_ISDIR(stats.st_mode)) {
  258. return 0;
  259. } else {
  260. warn("not a directory: %s", path);
  261. return -1;
  262. }
  263. }
  264. d = dir = (char*) s_malloc(strlen(path) + 1);
  265. strcpy(dir, path);
  266. while (d != NULL && !err) {
  267. d = strchr(d + 1, '/');
  268. if (d != NULL)
  269. *d = '\0';
  270. if (access(dir, F_OK) && errno == ENOENT) {
  271. if (mkdir(dir, 0755)) {
  272. warn("could not create directory: %s", dir);
  273. err = -1;
  274. }
  275. } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) {
  276. warn("not a directory: %s", dir);
  277. err = -1;
  278. }
  279. if (d != NULL)
  280. *d = '/';
  281. }
  282. free(dir);
  283. return err;
  284. }