A Simple X Image Viewer
 
 
 
 
 
 

338 line
6.4 KiB

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