A Simple X Image Viewer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

354 line
6.9 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(EXIT_FAILURE);
  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.0; i++)
  107. *size /= 1024.0;
  108. *unit = units[MIN(i, ARRLEN(units) - 1)];
  109. }
  110. void time_readable(float *time, const char **unit) {
  111. const char *units[] = { "s", "m", "h" };
  112. int i;
  113. for (i = 0; i < ARRLEN(units) && *time >= 60.0; i++)
  114. *time /= 60.0;
  115. *unit = units[MIN(i, ARRLEN(units) - 1)];
  116. }
  117. char* absolute_path(const char *filename) {
  118. size_t len;
  119. const char *basename;
  120. char *dir, *dirname = NULL, *path = NULL, *s;
  121. char *cwd = NULL, *twd = NULL;
  122. if (!filename || *filename == '\0' || *filename == '/')
  123. return NULL;
  124. len = FNAME_LEN;
  125. cwd = (char*) s_malloc(len);
  126. while (!(s = getcwd(cwd, len)) && errno == ERANGE) {
  127. len *= 2;
  128. cwd = (char*) s_realloc(cwd, len);
  129. }
  130. if (!s)
  131. goto error;
  132. s = strrchr(filename, '/');
  133. if (s) {
  134. len = s - filename;
  135. dirname = (char*) s_malloc(len + 1);
  136. strncpy(dirname, filename, len);
  137. dirname[len] = '\0';
  138. basename = s + 1;
  139. if (chdir(cwd))
  140. /* we're not able to come back afterwards */
  141. goto error;
  142. if (chdir(dirname))
  143. goto error;
  144. len = FNAME_LEN;
  145. twd = (char*) s_malloc(len);
  146. while (!(s = getcwd(twd, len)) && errno == ERANGE) {
  147. len *= 2;
  148. twd = (char*) s_realloc(twd, len);
  149. }
  150. if (chdir(cwd))
  151. die("could not revert to prior working directory");
  152. if (!s)
  153. goto error;
  154. dir = twd;
  155. } else {
  156. /* only a single filename given */
  157. basename = filename;
  158. dir = cwd;
  159. }
  160. len = strlen(dir) + strlen(basename) + 2;
  161. path = (char*) s_malloc(len);
  162. snprintf(path, len, "%s/%s", dir, basename);
  163. goto end;
  164. error:
  165. if (path) {
  166. free(path);
  167. path = NULL;
  168. }
  169. end:
  170. if (dirname)
  171. free(dirname);
  172. if (cwd)
  173. free(cwd);
  174. if (twd)
  175. free(twd);
  176. return path;
  177. }
  178. int r_opendir(r_dir_t *rdir, const char *dirname) {
  179. if (!rdir || !dirname || !*dirname)
  180. return -1;
  181. if (!(rdir->dir = opendir(dirname))) {
  182. rdir->name = NULL;
  183. rdir->stack = NULL;
  184. return -1;
  185. }
  186. rdir->stcap = DNAME_CNT;
  187. rdir->stack = (char**) s_malloc(rdir->stcap * sizeof(char*));
  188. rdir->stlen = 0;
  189. rdir->name = (char*) dirname;
  190. rdir->d = 0;
  191. return 0;
  192. }
  193. int r_closedir(r_dir_t *rdir) {
  194. int ret = 0;
  195. if (!rdir)
  196. return -1;
  197. if (rdir->stack) {
  198. while (rdir->stlen > 0)
  199. free(rdir->stack[--rdir->stlen]);
  200. free(rdir->stack);
  201. rdir->stack = NULL;
  202. }
  203. if (rdir->dir) {
  204. if (!(ret = closedir(rdir->dir)))
  205. rdir->dir = NULL;
  206. }
  207. if (rdir->d && rdir->name) {
  208. free(rdir->name);
  209. rdir->name = NULL;
  210. }
  211. return ret;
  212. }
  213. char* r_readdir(r_dir_t *rdir) {
  214. size_t len;
  215. char *filename;
  216. struct dirent *dentry;
  217. struct stat fstats;
  218. if (!rdir || !rdir->dir || !rdir->name)
  219. return NULL;
  220. while (1) {
  221. if (rdir->dir && (dentry = readdir(rdir->dir))) {
  222. if (STREQ(dentry->d_name, ".") || STREQ(dentry->d_name, ".."))
  223. continue;
  224. len = strlen(rdir->name) + strlen(dentry->d_name) + 2;
  225. filename = (char*) s_malloc(len);
  226. snprintf(filename, len, "%s%s%s", rdir->name,
  227. rdir->name[strlen(rdir->name)-1] == '/' ? "" : "/",
  228. dentry->d_name);
  229. if (!stat(filename, &fstats) && S_ISDIR(fstats.st_mode)) {
  230. /* put subdirectory on the stack */
  231. if (rdir->stlen == rdir->stcap) {
  232. rdir->stcap *= 2;
  233. rdir->stack = (char**) s_realloc(rdir->stack,
  234. rdir->stcap * sizeof(char*));
  235. }
  236. rdir->stack[rdir->stlen++] = filename;
  237. continue;
  238. }
  239. return filename;
  240. }
  241. if (rdir->stlen > 0) {
  242. /* open next subdirectory */
  243. closedir(rdir->dir);
  244. if (rdir->d)
  245. free(rdir->name);
  246. rdir->name = rdir->stack[--rdir->stlen];
  247. rdir->d = 1;
  248. if (!(rdir->dir = opendir(rdir->name)))
  249. warn("could not open directory: %s", rdir->name);
  250. continue;
  251. }
  252. /* no more entries */
  253. break;
  254. }
  255. return NULL;
  256. }
  257. int r_mkdir(const char *path) {
  258. char *dir, *d;
  259. struct stat stats;
  260. int err = 0;
  261. if (!path || !*path)
  262. return -1;
  263. if (!stat(path, &stats)) {
  264. if (S_ISDIR(stats.st_mode)) {
  265. return 0;
  266. } else {
  267. warn("not a directory: %s", path);
  268. return -1;
  269. }
  270. }
  271. d = dir = (char*) s_malloc(strlen(path) + 1);
  272. strcpy(dir, path);
  273. while (d != NULL && !err) {
  274. d = strchr(d + 1, '/');
  275. if (d != NULL)
  276. *d = '\0';
  277. if (access(dir, F_OK) && errno == ENOENT) {
  278. if (mkdir(dir, 0755)) {
  279. warn("could not create directory: %s", dir);
  280. err = -1;
  281. }
  282. } else if (stat(dir, &stats) || !S_ISDIR(stats.st_mode)) {
  283. warn("not a directory: %s", dir);
  284. err = -1;
  285. }
  286. if (d != NULL)
  287. *d = '/';
  288. }
  289. free(dir);
  290. return err;
  291. }