1 /* vifm
2 * Copyright (C) 2001 Ken Steen.
3 * Copyright (C) 2011 xaizek.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program 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 this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "utils.h"
21 #include "utils_int.h"
22
23 #ifdef _WIN32
24 #include <windows.h>
25 #include <winioctl.h>
26
27 #include "utf8.h"
28 #endif
29
30 #include <sys/types.h> /* pid_t */
31 #include <unistd.h>
32
33 #include <ctype.h> /* isalnum() isalpha() iscntrl() */
34 #include <errno.h> /* errno */
35 #include <math.h> /* modf() pow() */
36 #include <stddef.h> /* size_t */
37 #include <stdio.h> /* snprintf() */
38 #include <stdlib.h> /* free() malloc() qsort() */
39 #include <string.h> /* memcpy() strdup() strchr() strlen() strpbrk() strtol() */
40 #include <wchar.h> /* wcwidth() */
41
42 #include "../cfg/config.h"
43 #include "../compat/fs_limits.h"
44 #include "../compat/os.h"
45 #include "../engine/keys.h"
46 #include "../engine/variables.h"
47 #include "../int/fuse.h"
48 #include "../modes/dialogs/msg_dialog.h"
49 #include "../ui/cancellation.h"
50 #include "../ui/ui.h"
51 #include "../background.h"
52 #include "../registers.h"
53 #include "../status.h"
54 #include "env.h"
55 #include "file_streams.h"
56 #include "fs.h"
57 #include "log.h"
58 #include "macros.h"
59 #include "path.h"
60 #include "str.h"
61 #include "string_array.h"
62
63 static void show_progress_cb(const void *descr);
64 static const char ** get_size_suffixes(void);
65 static double split_size_double(double d, unsigned long long *ifraction,
66 int *fraction_width);
67 #ifdef _WIN32
68 static void unquote(char quoted[]);
69 #endif
70 static int is_line_spec(const char str[]);
71
72 /* Size suffixes of different kinds. */
73 static const char *iec_i_units[] = {
74 " B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"
75 };
76 static const char *iec_units[] = {
77 "B", "K", "M", "G", "T", "P", "E", "Z", "Y"
78 };
79 static const char *si_units[] = {
80 " B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"
81 };
82 ARRAY_GUARD(iec_units, ARRAY_LEN(iec_i_units));
83 ARRAY_GUARD(si_units, ARRAY_LEN(iec_units));
84
85 int
vifm_system(char command[],ShellRequester by)86 vifm_system(char command[], ShellRequester by)
87 {
88 #ifdef _WIN32
89 /* The check is primarily for tests, otherwise screen is reset. */
90 if(curr_stats.load_stage != 0)
91 {
92 system("cls");
93 }
94 #endif
95 LOG_INFO_MSG("Shell command: %s", command);
96 return run_in_shell_no_cls(command, by);
97 }
98
99 int
process_cmd_output(const char descr[],const char cmd[],int user_sh,int interactive,cmd_output_handler handler,void * arg)100 process_cmd_output(const char descr[], const char cmd[], int user_sh,
101 int interactive, cmd_output_handler handler, void *arg)
102 {
103 FILE *file, *err;
104 pid_t pid;
105 int nlines;
106 char **lines;
107 int i;
108
109 LOG_INFO_MSG("Capturing output of the command: %s", cmd);
110
111 pid = bg_run_and_capture((char *)cmd, user_sh, &file, &err);
112 if(pid == (pid_t)-1)
113 {
114 return 1;
115 }
116
117 ui_cancellation_push_on();
118
119 if(!interactive)
120 {
121 show_progress("", 0);
122 }
123
124 wait_for_data_from(pid, file, 0, &ui_cancellation_info);
125 lines = read_stream_lines(file, &nlines, 1,
126 interactive ? NULL : &show_progress_cb, descr);
127
128 ui_cancellation_pop();
129 fclose(file);
130
131 for(i = 0; i < nlines; ++i)
132 {
133 handler(lines[i], arg);
134 }
135 free_string_array(lines, nlines);
136
137 show_errors_from_file(err, descr);
138 return 0;
139 }
140
141 /* Callback implementation for read_stream_lines(). */
142 static void
show_progress_cb(const void * descr)143 show_progress_cb(const void *descr)
144 {
145 show_progress(descr, -250);
146 }
147
148 int
vifm_chdir(const char path[])149 vifm_chdir(const char path[])
150 {
151 char curr_path[PATH_MAX + 1];
152 if(get_cwd(curr_path, sizeof(curr_path)) == curr_path)
153 {
154 if(stroscmp(curr_path, path) == 0)
155 {
156 return 0;
157 }
158 }
159 return os_chdir(path);
160 }
161
162 char *
expand_path(const char path[])163 expand_path(const char path[])
164 {
165 char *const expanded_envvars = expand_envvars(path, 0);
166 /* replace_tilde() frees memory pointed to by expand_envvars. */
167 return replace_tilde(expanded_envvars);
168 }
169
170 char *
expand_envvars(const char str[],int escape_vals)171 expand_envvars(const char str[], int escape_vals)
172 {
173 char *result = NULL;
174 size_t len = 0;
175 int prev_slash = 0;
176 while(*str != '\0')
177 {
178 if(!prev_slash && *str == '$' && isalpha(str[1]))
179 {
180 char var_name[NAME_MAX + 1];
181 const char *p = str + 1;
182 char *q = var_name;
183 const char *var_value;
184
185 while((isalnum(*p) || *p == '_') &&
186 (size_t)(q - var_name) < sizeof(var_name) - 1)
187 {
188 *q++ = *p++;
189 }
190 *q = '\0';
191
192 var_value = local_getenv(var_name);
193 if(!is_null_or_empty(var_value))
194 {
195 char *escaped_var_value = NULL;
196 if(escape_vals)
197 {
198 escaped_var_value = shell_like_escape(var_value, 2);
199 var_value = escaped_var_value;
200 }
201
202 result = extend_string(result, var_value, &len);
203 free(escaped_var_value);
204
205 str = p;
206 }
207 else
208 {
209 str++;
210 }
211 }
212 else
213 {
214 prev_slash = (*str == '\\') ? !prev_slash : 0;
215
216 if(!prev_slash || escape_vals)
217 {
218 const char single_char[] = { *str, '\0' };
219 result = extend_string(result, single_char, &len);
220 }
221
222 str++;
223 }
224 }
225 if(result == NULL)
226 result = strdup("");
227 return result;
228 }
229
230 int
friendly_size_notation(uint64_t num,int str_size,char str[])231 friendly_size_notation(uint64_t num, int str_size, char str[])
232 {
233 unsigned long long fraction = 0ULL;
234 int fraction_width;
235 const char **const units = get_size_suffixes();
236 size_t u = 0U;
237 double d = num;
238
239 while(d >= cfg.sizefmt.base - 0.5 && u < ARRAY_LEN(iec_i_units) - 1U)
240 {
241 d /= cfg.sizefmt.base;
242 ++u;
243 }
244
245 /* Fractional part is ignored when it's absent (we didn't divide anything) and
246 * when we format size in previously used manner and hide it for values
247 * greater than 9. */
248 if(u != 0U && !(cfg.sizefmt.precision == 0 && d > 9.0))
249 {
250 d = split_size_double(d, &fraction, &fraction_width);
251 }
252
253 if(fraction == 0)
254 {
255 snprintf(str, str_size, "%.0f%s%s", d, cfg.sizefmt.space ? " " : "",
256 units[u]);
257 }
258 else
259 {
260 snprintf(str, str_size, "%.0f.%0*" PRINTF_ULL "%s%s", d, fraction_width,
261 fraction, cfg.sizefmt.space ? " " : "", units[u]);
262 }
263
264 return u > 0U;
265 }
266
267 /* Picks size suffixes as per configuration. Returns one of *_units arrays. */
268 static const char **
get_size_suffixes(void)269 get_size_suffixes(void)
270 {
271 return (cfg.sizefmt.base == 1000) ? si_units :
272 (cfg.sizefmt.ieci_prefixes ? iec_i_units : iec_units);
273 }
274
275 /* Breaks size (floating point, not integer) into integer and fractional parts
276 * rounding and truncating them as necessary. Sets *ifraction and
277 * *fraction_width. Returns new value for the size (truncated and/or
278 * rounded). */
279 static double
split_size_double(double size,unsigned long long * ifraction,int * fraction_width)280 split_size_double(double size, unsigned long long *ifraction,
281 int *fraction_width)
282 {
283 double ten_power, dfraction, integer;
284
285 *fraction_width = (cfg.sizefmt.precision == 0) ? 1 : cfg.sizefmt.precision;
286
287 ten_power = pow(10, *fraction_width + 1);
288 while(ten_power > ULLONG_MAX)
289 {
290 ten_power /= 10;
291 --*fraction_width;
292 }
293
294 dfraction = modf(size, &integer);
295 *ifraction = DIV_ROUND_UP(ten_power*dfraction, 10);
296
297 if(*ifraction >= ten_power/10.0)
298 {
299 /* Overflow into integer part during rounding. */
300 integer += 1.0;
301 *ifraction -= ten_power/10.0;
302 }
303 else if(*ifraction == 0 && dfraction != 0.0)
304 {
305 /* Fractional part is too small, "round up" to make it visible. */
306 *ifraction = 1;
307 }
308
309 /* Skip trailing zeroes. */
310 for(; *fraction_width != 0 && *ifraction%10 == 0; --*fraction_width)
311 {
312 *ifraction /= 10;
313 }
314
315 return (*ifraction == 0) ? size : integer;
316 }
317
318 const char *
enclose_in_dquotes(const char str[])319 enclose_in_dquotes(const char str[])
320 {
321 static char buf[1 + PATH_MAX*2 + 1 + 1];
322 char *p;
323
324 p = buf;
325 *p++ = '"';
326 while(*str != '\0')
327 {
328 if((curr_stats.shell_type != ST_PS && *str == '\\') || *str == '"' ||
329 (curr_stats.shell_type == ST_NORMAL && (*str == '$' || *str == '`')))
330 {
331 *p++ = '\\';
332 }
333 *p++ = *str;
334
335 str++;
336 }
337 *p++ = '"';
338 *p = '\0';
339 return buf;
340 }
341
342 const char *
make_name_unique(const char filename[])343 make_name_unique(const char filename[])
344 {
345 static char unique[PATH_MAX + 1];
346 size_t len;
347 int i;
348
349 #ifndef _WIN32
350 snprintf(unique, sizeof(unique), "%s_%u%u_00", filename, getppid(),
351 get_pid());
352 #else
353 /* TODO: think about better name uniqualization on Windows. */
354 snprintf(unique, sizeof(unique), "%s_%u_00", filename, get_pid());
355 #endif
356 len = strlen(unique);
357 i = 0;
358
359 while(path_exists(unique, DEREF))
360 {
361 sprintf(unique + len - 2, "%d", ++i);
362 }
363 return unique;
364 }
365
366 char *
extract_cmd_name(const char line[],int raw,size_t buf_len,char buf[])367 extract_cmd_name(const char line[], int raw, size_t buf_len, char buf[])
368 {
369 const char *result;
370 #ifdef _WIN32
371 int left_quote, right_quote = 0;
372 #endif
373
374 line = skip_whitespace(line);
375
376 #ifdef _WIN32
377 if((left_quote = (line[0] == '"')))
378 {
379 result = strchr(line + 1, '"');
380 }
381 else
382 #endif
383 {
384 result = strchr(line, ' ');
385 }
386 if(result == NULL)
387 {
388 result = line + strlen(line);
389 }
390
391 #ifdef _WIN32
392 if(left_quote && (right_quote = (result[0] == '"')))
393 {
394 result++;
395 }
396 #endif
397 copy_str(buf, MIN((size_t)(result - line + 1), buf_len), line);
398 #ifdef _WIN32
399 if(!raw && left_quote && right_quote)
400 {
401 unquote(buf);
402 }
403 #endif
404 if(!raw)
405 {
406 fuse_strip_mount_metadata(buf);
407 }
408 result = skip_whitespace(result);
409
410 return (char *)result;
411 }
412
413 #ifdef _WIN32
414 /* Removes first and the last charater of the string, if they are quotes. */
415 static void
unquote(char quoted[])416 unquote(char quoted[])
417 {
418 size_t len = strlen(quoted);
419 if(len > 2 && quoted[0] == quoted[len - 1] && strpbrk(quoted, "\"'`") != NULL)
420 {
421 memmove(quoted, quoted + 1, len - 2);
422 quoted[len - 2] = '\0';
423 }
424 }
425 #endif
426
427 int
vifm_wcwidth(wchar_t wc)428 vifm_wcwidth(wchar_t wc)
429 {
430 const int width = wcwidth(wc);
431 if(width == -1)
432 {
433 return ((size_t)wc < (size_t)L' ') ? 2 : 1;
434 }
435 return width;
436 }
437
438 int
vifm_wcswidth(const wchar_t str[],size_t n)439 vifm_wcswidth(const wchar_t str[], size_t n)
440 {
441 int width = 0;
442 while(*str != L'\0' && n--)
443 {
444 width += vifm_wcwidth(*str++);
445 }
446 return width;
447 }
448
449 char *
escape_for_squotes(const char string[],size_t offset)450 escape_for_squotes(const char string[], size_t offset)
451 {
452 /* TODO: maybe combine code with escape_for_dquotes(). */
453
454 size_t len;
455 char *escaped, *out;
456
457 len = strlen(string);
458
459 escaped = malloc(len*2 + 1);
460 out = escaped;
461
462 /* Copy prefix not escaping it. */
463 offset = MIN(len, offset);
464 memcpy(out, string, offset);
465 out += offset;
466 string += offset;
467
468 while(*string != '\0')
469 {
470 if(*string == '\'')
471 {
472 *out++ = '\'';
473 }
474
475 *out++ = *string++;
476 }
477 *out = '\0';
478 return escaped;
479 }
480
481 char *
escape_for_dquotes(const char string[],size_t offset)482 escape_for_dquotes(const char string[], size_t offset)
483 {
484 /* TODO: maybe combine code with escape_for_squotes(). */
485
486 size_t len;
487 char *escaped, *out;
488
489 len = strlen(string);
490
491 escaped = malloc(len*2 + 1);
492 out = escaped;
493
494 /* Copy prefix not escaping it. */
495 offset = MIN(len, offset);
496 memcpy(out, string, offset);
497 out += offset;
498 string += offset;
499
500 while(*string != '\0')
501 {
502 switch(*string)
503 {
504 case '\a': *out++ = '\\'; *out++ = 'a'; break;
505 case '\b': *out++ = '\\'; *out++ = 'b'; break;
506 case '\f': *out++ = '\\'; *out++ = 'f'; break;
507 case '\n': *out++ = '\\'; *out++ = 'n'; break;
508 case '\r': *out++ = '\\'; *out++ = 'r'; break;
509 case '\t': *out++ = '\\'; *out++ = 't'; break;
510 case '\v': *out++ = '\\'; *out++ = 'v'; break;
511 case '"': *out++ = '\\'; *out++ = '"'; break;
512
513 default:
514 *out++ = *string;
515 break;
516 }
517 ++string;
518 }
519 *out = '\0';
520 return escaped;
521 }
522
523 char *
escape_unreadable(const char str[])524 escape_unreadable(const char str[])
525 {
526 char *escaped = malloc(strlen(str)*2 + 1);
527
528 char *out = escaped;
529 while(*str != '\0')
530 {
531 if(iscntrl((unsigned char)*str))
532 {
533 *out++ = '^';
534 *out++ = *str ^ 64;
535 }
536 else
537 {
538 *out++ = *str;
539 }
540 ++str;
541 }
542 *out = '\0';
543
544 return escaped;
545 }
546
547 void
expand_percent_escaping(char s[])548 expand_percent_escaping(char s[])
549 {
550 char *p;
551
552 p = s;
553 while(s[0] != '\0')
554 {
555 if(s[0] == '%' && s[1] == '%')
556 {
557 ++s;
558 }
559 *p++ = *s++;
560 }
561 *p = '\0';
562 }
563
564 void
expand_squotes_escaping(char s[])565 expand_squotes_escaping(char s[])
566 {
567 char *p;
568 int sq_found;
569
570 p = s++;
571 sq_found = *p == '\'';
572 while(*p != '\0')
573 {
574 if(*s == '\'' && sq_found)
575 {
576 sq_found = 0;
577 }
578 else
579 {
580 *++p = *s;
581 sq_found = *s == '\'';
582 }
583 s++;
584 }
585 }
586
587 void
expand_dquotes_escaping(char s[])588 expand_dquotes_escaping(char s[])
589 {
590 static const char table[] =
591 /* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */
592 /* 00 */ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
593 /* 10 */ "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
594 /* 20 */ "\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
595 /* 30 */ "\x00\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
596 /* 40 */ "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
597 /* 50 */ "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
598 /* 60 */ "\x60\x07\x0b\x63\x64\x65\x0c\x67\x68\x69\x6a\x6b\x6c\x6d\x0a\x6f"
599 /* 70 */ "\x70\x71\x0d\x73\x09\x75\x0b\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
600 /* 80 */ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
601 /* 90 */ "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
602 /* a0 */ "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
603 /* b0 */ "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
604 /* c0 */ "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
605 /* d0 */ "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
606 /* e0 */ "\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
607 /* f0 */ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
608
609 char *str = s;
610 char *p;
611
612 p = s;
613 while(*s != '\0')
614 {
615 if(*s != '\\')
616 {
617 *p++ = *s++;
618 continue;
619 }
620 s++;
621 if(*s == '\0')
622 {
623 LOG_ERROR_MSG("Escaped eol in \"%s\"", str);
624 break;
625 }
626 *p++ = table[(int)*s++];
627 }
628 *p = '\0';
629 }
630
631 int
def_reg(int reg)632 def_reg(int reg)
633 {
634 return (reg == NO_REG_GIVEN) ? DEFAULT_REG_NAME : reg;
635 }
636
637 int
def_count(int count)638 def_count(int count)
639 {
640 return (count == NO_COUNT_GIVEN) ? 1 : count;
641 }
642
643 char *
parse_line_for_path(const char line[],const char cwd[])644 parse_line_for_path(const char line[], const char cwd[])
645 {
646 int line_num;
647 /* Skip empty lines. */
648 return (skip_whitespace(line)[0] == '\0')
649 ? NULL
650 : parse_file_spec(line, &line_num, cwd);
651 }
652
653 char *
parse_file_spec(const char spec[],int * line_num,const char cwd[])654 parse_file_spec(const char spec[], int *line_num, const char cwd[])
655 {
656 char *path_buf;
657 const char *colon;
658 const size_t bufs_len = strlen(cwd) + 1U + strlen(spec) + 1U + 1U;
659 char canonicalized[PATH_MAX + 1];
660
661 path_buf = malloc(bufs_len);
662 if(path_buf == NULL)
663 {
664 return NULL;
665 }
666
667 if(is_path_absolute(spec) || spec[0] == '~')
668 {
669 path_buf[0] = '\0';
670 }
671 else
672 {
673 snprintf(path_buf, bufs_len, "%s/", cwd);
674 }
675
676 #ifdef _WIN32
677 colon = strchr(spec + (is_path_absolute(spec) ? 2 : 0), ':');
678 if(colon != NULL && !is_line_spec(colon + 1))
679 {
680 colon = NULL;
681 }
682 #else
683 colon = strchr(spec, ':');
684 while(colon != NULL)
685 {
686 if(is_line_spec(colon + 1))
687 {
688 char path[bufs_len];
689 strcpy(path, path_buf);
690 strncat(path, spec, colon - spec);
691 if(path_exists(path, NODEREF))
692 {
693 break;
694 }
695 }
696
697 colon = strchr(colon + 1, ':');
698 }
699 #endif
700
701 if(colon != NULL)
702 {
703 strncat(path_buf, spec, colon - spec);
704 *line_num = atoi(colon + 1);
705 }
706 else
707 {
708 strcat(path_buf, spec);
709 *line_num = 1;
710
711 while(!path_exists(path_buf, NODEREF) && strchr(path_buf, ':') != NULL)
712 {
713 break_atr(path_buf, ':');
714 }
715 }
716
717 chomp(path_buf);
718 canonicalize_path(path_buf, canonicalized, sizeof(canonicalized));
719
720 system_to_internal_slashes(canonicalized);
721
722 if(!ends_with_slash(path_buf) && !is_root_dir(canonicalized) &&
723 strcmp(canonicalized, "./") != 0)
724 {
725 chosp(canonicalized);
726 }
727
728 free(path_buf);
729
730 return replace_tilde(strdup(canonicalized));
731 }
732
733 /* Checks whether str points to a valid line number. Returns non-zero if so,
734 * otherwise zero is returned. */
735 static int
is_line_spec(const char str[])736 is_line_spec(const char str[])
737 {
738 char *endptr;
739 errno = 0;
740 (void)strtol(str, &endptr, 10);
741 return (endptr != str && errno == 0 && *endptr == ':');
742 }
743
744 void
safe_qsort(void * base,size_t nmemb,size_t size,int (* compar)(const void *,const void *))745 safe_qsort(void *base, size_t nmemb, size_t size,
746 int (*compar)(const void *, const void *))
747 {
748 if(nmemb != 0U)
749 {
750 /* Even when there are no entries, qsort() shouldn't be called with a NULL
751 * parameter. It isn't allowed by the standard. */
752 qsort(base, nmemb, size, compar);
753 }
754 }
755
756 /* vim: set tabstop=2 softtabstop=2 shiftwidth=2 noexpandtab cinoptions-=(0 : */
757 /* vim: set cinoptions+=t0 filetype=c : */
758