1 /* aux.c -- functions that do not fit in any other file */
2 
3 /*
4  * This file is part of CliFM
5  *
6  * Copyright (C) 2016-2021, L. Abramovich <johndoe.arch@outlook.com>
7  * All rights reserved.
8 
9  * CliFM is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * CliFM is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22  * MA 02110-1301, USA.
23  */
24 
25 #include "helpers.h"
26 
27 #include <ctype.h>
28 #include <dirent.h>
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <termios.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <time.h>
38 #include <limits.h>
39 #include <readline/readline.h>
40 
41 #include "aux.h"
42 #include "exec.h"
43 #include "misc.h"
44 #ifndef _NO_HIGHLIGHT
45 #include "highlight.h"
46 #endif
47 
48 #ifdef RL81
49 /* Sleep for MSEC milliseconds */
50 /* Taken from https://stackoverflow.com/questions/1157209/is-there-an-alternative-sleep-function-in-c-to-milliseconds */
51 static int
msleep(long msec)52 msleep(long msec)
53 {
54 	struct timespec ts;
55 	int res;
56 
57 	if (msec < 0) {
58 		errno = EINVAL;
59 		return (-1);
60 	}
61 
62 	ts.tv_sec = msec / 1000;
63 	ts.tv_nsec = (msec % 1000) * 1000000;
64 
65 	do {
66 		res = nanosleep(&ts, &ts);
67 	} while (res && errno == EINTR);
68 
69 	return res;
70 }
71 #endif
72 
73 void
rl_ring_bell(void)74 rl_ring_bell(void)
75 {
76 	switch(bell) {
77 	case BELL_NONE: return;
78 
79 	case BELL_AUDIBLE:
80 		fputs("\007", stderr);
81 		fflush(stderr);
82 		return;
83 
84 /* rl_activate_mark and rl_deactivate mark are available only since
85  * readline 8.1 */
86 #ifdef RL81
87 	case BELL_VISIBLE: {
88 		int point = rl_point;
89 		rl_mark = rl_last_word_start;
90 		if (rl_end > 1 && rl_line_buffer[rl_end - 1] == ' ')
91 			rl_point--;
92 		rl_activate_mark();
93 		rl_redisplay();
94 		msleep(VISIBLE_BELL_DELAY);
95 		rl_deactivate_mark();
96 #ifndef _NO_HIGHLIGHT
97 		if (highlight && !wrong_cmd) {
98 			rl_point = rl_mark;
99 			recolorize_line();
100 		}
101 #endif /* !_NO_HIGHLIGHT */
102 		rl_point = point;
103 		return;
104 		}
105 #endif /* RL81 */
106 
107 	default: return;
108 	}
109 
110 	return;
111 }
112 
113 /* The following three functions were taken from
114  * https://github.com/antirez/linenoise/blob/master/linenoise.c
115  * and modified to fir our needs: they are used to get current cursor
116  * position (both vertical and horizontal) by the suggestions system */
117 
118 /* Set the terminal into raw mode. Return 0 on success and -1 on error */
119 static int
enable_raw_mode(const int fd)120 enable_raw_mode(const int fd)
121 {
122 	struct termios raw;
123 
124 	if (!isatty(STDIN_FILENO))
125 		goto FAIL;
126 
127 	if (tcgetattr(fd, &orig_termios) == -1)
128 		goto FAIL;
129 
130 	raw = orig_termios;  /* modify the original mode */
131 	/* input modes: no break, no CR to NL, no parity check, no strip char,
132 	 * * no start/stop output control. */
133 	raw.c_iflag &= (tcflag_t)~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
134 	/* output modes - disable post processing */
135 	raw.c_oflag &= (tcflag_t)~(OPOST);
136 	/* control modes - set 8 bit chars */
137 	raw.c_cflag |= (CS8);
138 	/* local modes - choing off, canonical off, no extended functions,
139 	 * no signal chars (^Z,^C) */
140 	raw.c_lflag &= (tcflag_t)~(ECHO | ICANON | IEXTEN | ISIG);
141     /* control chars - set return condition: min number of bytes and timer.
142      * We want read to return every single byte, without timeout. */
143 	raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
144 
145 	/* put terminal in raw mode after flushing */
146 	if (tcsetattr(fd, TCSAFLUSH, &raw) < 0)
147 		goto FAIL;
148 
149 	return 0;
150 
151 FAIL:
152 	errno = ENOTTY;
153 	return -1;
154 }
155 
156 static int
disable_raw_mode(const int fd)157 disable_raw_mode(const int fd)
158 {
159 	if (tcsetattr(fd, TCSAFLUSH, &orig_termios) != -1)
160 		return EXIT_SUCCESS;
161 	return EXIT_FAILURE;
162 }
163 
164 /* Use the "ESC [6n" escape sequence to query the cursor position (both
165  * vertical and horizontal) and store both values into global variables.
166  * Return 0 on success and 1 on error */
167 int
get_cursor_position(const int ifd,const int ofd)168 get_cursor_position(const int ifd, const int ofd)
169 {
170 	char buf[32];
171 	int cols, rows;
172 	unsigned int i = 0;
173 
174 	if (enable_raw_mode(ifd) == -1)
175 		return EXIT_FAILURE;
176 
177 	/* Report cursor location */
178 	if (write(ofd, "\x1b[6n", 4) != 4)
179 		goto FAIL;
180 
181 	/* Read the response: "ESC [ rows ; cols R" */
182 	while (i < sizeof(buf) - 1) {
183 		if (read(ifd, buf + i, 1) != 1)
184 			break;
185 		if (buf[i] == 'R')
186 			break;
187 		i++;
188 	}
189 	buf[i] = '\0';
190 
191 	/* Parse it */
192 	if (buf[0] != _ESC || buf[1] != '[')
193 		goto FAIL;
194 	if (sscanf(buf + 2, "%d;%d", &rows, &cols) != 2)
195 		goto FAIL;
196 
197 	currow = rows;
198 	curcol = cols;
199 
200 	disable_raw_mode(ifd);
201 	return EXIT_SUCCESS;
202 
203 FAIL:
204 	disable_raw_mode(ifd);
205 	return EXIT_FAILURE;
206 }
207 /*
208 int
209 get_term_bgcolor(const int ifd, const int ofd)
210 {
211 	char buf[32] = {0};
212 	unsigned int i = 0;
213 
214 	if (enable_raw_mode(ifd) == -1)
215 		return EXIT_FAILURE;
216 
217 	// Report terminal background color
218 	if (write(ofd, "\x1b]11;?\007", 7) != 7)
219 		goto FAIL;
220 
221 	// Read the response: "ESC ] 11 ; rgb:COLOR BEL"
222 	while (i < sizeof(buf) - 1) {
223 		if (i > 22)
224 			break;
225 		if (read(ifd, buf + i, 1) != 1)
226 			break;
227 		printf("%d:'%c'\n", buf[i], buf[i]);
228 		i++;
229 	}
230 	buf[i] = '\0';
231 
232 	char *p = strchr(buf, ':');
233 	if (!p || !*(++p))
234 		goto FAIL;
235 	term_bgcolor = savestring(p, strlen(p));
236 
237 	disable_raw_mode(ifd);
238 	return EXIT_SUCCESS;
239 
240 FAIL:
241 	disable_raw_mode(ifd);
242 	return EXIT_FAILURE;
243 } */
244 
245 char *
gen_date_suffix(struct tm tm)246 gen_date_suffix(struct tm tm)
247 {
248 	char date[64] = "";
249 	strftime(date, sizeof(date), "%b %d %H:%M:%S %Y", &tm);
250 
251 	char *suffix = (char *)xnmalloc(68, sizeof(char));
252 	snprintf(suffix, 67, "%d%d%d%d%d%d", tm.tm_year + 1900,
253 	    tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
254 
255 	return suffix;
256 }
257 
258 /* Create directory DIR with permissions set to MODE */
259 int
xmkdir(char * dir,mode_t mode)260 xmkdir(char *dir, mode_t mode)
261 {
262 	mode_t old_mask = umask(0);
263 	int ret = mkdirat(AT_FDCWD, dir, mode);
264 	umask(old_mask);
265 	if (ret == -1)
266 		return EXIT_FAILURE;
267 	return EXIT_SUCCESS;
268 }
269 
270 /* Open a file for read only. Return a file stream associated to a file
271  * descriptor (FD) for the file named NAME */
272 FILE *
open_fstream_r(char * name,int * fd)273 open_fstream_r(char *name, int *fd)
274 {
275 	if (!name || !*name)
276 		return (FILE *)NULL;
277 
278 	*fd = open(name, O_RDONLY);
279 	if (*fd == -1)
280 		return (FILE *)NULL;
281 
282 	FILE *fp = fdopen(*fd, "r");
283 	if (!fp) {
284 		close(*fd);
285 		return (FILE *)NULL;
286 	}
287 
288 	return fp;
289 }
290 
291 /* Create a file for writing. Return a file stream associated to a file
292  * descriptor (FD) for the file named NAME */
293 FILE *
open_fstream_w(char * name,int * fd)294 open_fstream_w(char *name, int *fd)
295 {
296 	if (!name || !*name)
297 		return (FILE *)NULL;
298 
299 	*fd = open(name, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
300 	if (*fd == -1)
301 		return (FILE *)NULL;
302 
303 	FILE *fp = fdopen(*fd, "w");
304 	if (!fp) {
305 		close(*fd);
306 		return (FILE *)NULL;
307 	}
308 
309 	return fp;
310 }
311 
312 /* Close file stream FP and file descriptor FD */
313 void
close_fstream(FILE * fp,int fd)314 close_fstream(FILE *fp, int fd)
315 {
316 	fclose(fp);
317 	close(fd);
318 }
319 
320 /* Transform S_IFXXX (MODE) into the corresponding DT_XXX constant */
321 inline mode_t
get_dt(const mode_t mode)322 get_dt(const mode_t mode)
323 {
324 	switch (mode & S_IFMT) {
325 	case S_IFBLK: return DT_BLK;
326 	case S_IFCHR: return DT_CHR;
327 	case S_IFDIR: return DT_DIR;
328 	case S_IFIFO: return DT_FIFO;
329 	case S_IFLNK: return DT_LNK;
330 	case S_IFREG: return DT_REG;
331 	case S_IFSOCK: return DT_SOCK;
332 	default: return DT_UNKNOWN;
333 	}
334 }
335 
336 /*
337 static int
338 hex2int(char *str)
339 {
340 	int i, n[2] = { 0 };
341 	for (i = 1; i >= 0; i--) {
342 		if (str[i] >= '0' && str[i] <= '9') {
343 			n[i] = str[i] - '0';
344 		} else {
345 			switch (str[i]) {
346 			case 'A':
347 			case 'a':
348 				n[i] = 10;
349 				break;
350 			case 'B':
351 			case 'b':
352 				n[i] = 11;
353 				break;
354 			case 'C':
355 			case 'c':
356 				n[i] = 12;
357 				break;
358 			case 'D':
359 			case 'd':
360 				n[i] = 13;
361 				break;
362 			case 'E':
363 			case 'e':
364 				n[i] = 14;
365 				break;
366 			case 'F':
367 			case 'f':
368 				n[i] = 15;
369 				break;
370 			default:
371 				break;
372 			}
373 		}
374 	}
375 
376 	return ((n[0] * 16) + n[1]);
377 } */
378 
379 /* Given this value: \xA0\xA1\xA2, return an array of integers with
380  * the integer values for A0, A1, and A2 respectivelly */
381 /*int *
382 get_hex_num(const char *str)
383 {
384 	size_t i = 0;
385 	int *hex_n = (int *)xnmalloc(3, sizeof(int));
386 
387 	while (*str) {
388 		if (*str != '\\') {
389 			str++;
390 			continue;
391 		}
392 
393 		if (*(str + 1) != 'x')
394 			break;
395 
396 		str += 2;
397 		char *tmp = xnmalloc(3, sizeof(char));
398 		xstrsncpy(tmp, str, 2);
399 
400 		if (i >= 3)
401 			hex_n = xrealloc(hex_n, (i + 1) * sizeof(int *));
402 
403 		hex_n[i++] = hex2int(tmp);
404 
405 		free(tmp);
406 		tmp = (char *)NULL;
407 		str++;
408 	}
409 
410 	hex_n = xrealloc(hex_n, (i + 1) * sizeof(int));
411 	hex_n[i] = -1; // -1 marks the end of the int array
412 
413 	return hex_n;
414 } */
415 
416 /* Count files in DIR_PATH, including self and parent. If POP is set to 1,
417  * The function will just check if the directory is populated (it has at
418  * least 3 files, including self and parent)*/
419 int
count_dir(const char * dir,int pop)420 count_dir(const char *dir, int pop)
421 {
422 	if (!dir)
423 		return (-1);
424 
425 	DIR *p;
426 	if ((p = opendir(dir)) == NULL) {
427 		if (errno == ENOMEM)
428 			exit(EXIT_FAILURE);
429 		else
430 			return (-1);
431 	}
432 
433 	int c = 0;
434 
435 	while (readdir(p)) {
436 		c++;
437 		if (pop && c > 2)
438 			break;
439 	}
440 
441 	closedir(p);
442 	return c;
443 }
444 
445 /* Get the path of a given command from the PATH environment variable.
446  * It basically does the same as the 'which' Unix command */
447 char *
get_cmd_path(const char * cmd)448 get_cmd_path(const char *cmd)
449 {
450 	char *cmd_path = (char *)xnmalloc(PATH_MAX + 1, sizeof(char));
451 
452 	size_t i;
453 	for (i = 0; i < path_n; i++) { /* Check each path in PATH */
454 		/* Append cmd to each path and check if it exists and is
455 		 * executable */
456 		snprintf(cmd_path, PATH_MAX, "%s/%s", paths[i], cmd);
457 		if (access(cmd_path, X_OK) == 0)
458 			return cmd_path;
459 	}
460 
461 	free(cmd_path);
462 	return (char *)NULL;
463 }
464 
465 /* Convert FILE_SIZE to human readeable form */
466 char *
get_size_unit(off_t size)467 get_size_unit(off_t size)
468 {
469 #define MAX_UNIT_SIZE 9
470 	/* Max size type length == 9 == "1023.99K\0" */
471 	char *str = xnmalloc(MAX_UNIT_SIZE, sizeof(char));
472 
473 	size_t n = 0;
474 	float s = (float)size;
475 
476 	while (s > 1024) {
477 		s = s / 1024;
478 		++n;
479 	}
480 
481 	int x = (int)s;
482 	/* If s - x == 0, then S has no reminder (zero)
483 	 * We don't want to print the reminder when it is zero */
484 
485 	const char *const u = "BKMGTPEZY";
486 	snprintf(str, MAX_UNIT_SIZE, "%.*f%c", (s == 0 || s - (float)x == 0)
487 			? 0 : 2, (double)s, u[n]);
488 
489 	return str;
490 }
491 
492 off_t
dir_size(char * dir)493 dir_size(char *dir)
494 {
495 	if (!dir || !*dir)
496 		return (-1);
497 
498 	char file[PATH_MAX];
499 	snprintf(file, PATH_MAX, "%s/duXXXXXX", P_tmpdir);
500 
501 	int fd = mkstemp(file);
502 	if (fd == -1)
503 		return (-1);
504 
505 	int stdout_bk = dup(STDOUT_FILENO); /* Save original stdout */
506 	dup2(fd, STDOUT_FILENO); /* Redirect stdout to the desired file */
507 	close(fd);
508 
509 	char *cmd[] = {"du", "-ks", dir, NULL};
510 	launch_execve(cmd, FOREGROUND, E_NOSTDERR);
511 
512 	dup2(stdout_bk, STDOUT_FILENO); /* Restore original stdout */
513 	close(stdout_bk);
514 
515 	FILE *fp = open_fstream_r(file, &fd);
516 	if (!fp) {
517 		unlink(file);
518 		return (-1);
519 	}
520 
521 	off_t retval = -1;
522 	/* I only need here the first field of the line, which is a
523 	 * file size and could only take a few bytes, so that 32
524 	 * bytes is more than enough */
525 	char line[32];
526 	if (fgets(line, (int)sizeof(line), fp) == NULL) {
527 		close_fstream(fp, fd);
528 		unlink(file);
529 		return (-1);
530 	}
531 
532 	char *p = strchr(line, '\t');
533 	if (p && p != line) {
534 		*p = '\0';
535 		retval = (off_t)atoll(line);
536 	}
537 
538 	close_fstream(fp, fd);
539 	unlink(file);
540 	return retval;
541 }
542 
543 /* Return the file type of the file pointed to by LINK, or -1 in case of
544  * error. Possible return values:
545 S_IFDIR: 40000 (octal) / 16384 (decimal, integer)
546 S_IFREG: 100000 / 32768
547 S_IFLNK: 120000 / 40960
548 S_IFSOCK: 140000 / 49152
549 S_IFBLK: 60000 / 24576
550 S_IFCHR: 20000 / 8192
551 S_IFIFO: 10000 / 4096
552  * See the inode manpage */
553 int
get_link_ref(const char * link)554 get_link_ref(const char *link)
555 {
556 	if (!link)
557 		return (-1);
558 
559 	char *linkname = realpath(link, (char *)NULL);
560 	if (!linkname)
561 		return (-1);
562 
563 	struct stat attr;
564 	int ret = stat(linkname, &attr);
565 	free(linkname);
566 	if (ret == -1)
567 		return (-1);
568 	return (int)(attr.st_mode & S_IFMT);
569 }
570 
571 /* Transform an integer (N) into a string of chars
572  * This exists because some Operating systems do not support itoa */
573 char *
xitoa(int n)574 xitoa(int n)
575 {
576 	if (!n)
577 		return "0";
578 
579 	static char buf[32] = {0};
580 	int i = 30;
581 
582 	while (n && i) {
583 		int rem = n / 10;
584 		buf[i] = (char)('0' + (n - (rem * 10)));
585 		n = rem;
586 		--i;
587 	}
588 
589 	return &buf[++i];
590 }
591 
592 int
xatoi(const char * s)593 xatoi(const char *s)
594 {
595 	long ret = strtol(s, NULL, 10);
596 	if (ret == LONG_MAX || ret == LONG_MIN) {
597 		fprintf(stderr, "%s: strtol: %s: %s\n", PROGRAM_NAME, s, strerror(errno));
598 		exit(EXIT_FAILURE);
599 	}
600 	return (int)ret;
601 }
602 
603 /* Some memory wrapper functions */
604 void *
xrealloc(void * ptr,size_t size)605 xrealloc(void *ptr, size_t size)
606 {
607 	void *p = realloc(ptr, size);
608 
609 	if (!p) {
610 		_err(0, NOPRINT_PROMPT, _("%s: %s failed to allocate %zu bytes\n"),
611 				PROGRAM_NAME, __func__, size);
612 		exit(EXIT_FAILURE);
613 	}
614 
615 	return p;
616 }
617 
618 void *
xcalloc(size_t nmemb,size_t size)619 xcalloc(size_t nmemb, size_t size)
620 {
621 	void *p = calloc(nmemb, size);
622 
623 	if (!p) {
624 		_err(0, NOPRINT_PROMPT, _("%s: %s failed to allocate %zu bytes\n"),
625 				PROGRAM_NAME, __func__, nmemb * size);
626 		exit(EXIT_FAILURE);
627 	}
628 
629 	return p;
630 }
631 
632 void *
xnmalloc(size_t nmemb,size_t size)633 xnmalloc(size_t nmemb, size_t size)
634 {
635 	void *p = malloc(nmemb * size);
636 
637 	if (!p) {
638 		_err(0, NOPRINT_PROMPT, _("%s: %s failed to allocate %zu bytes\n"),
639 				PROGRAM_NAME, __func__, nmemb * size);
640 		exit(EXIT_FAILURE);
641 	}
642 
643 	return p;
644 }
645 
646 /* Unlike getchar this does not wait for newline('\n')
647 https://stackoverflow.com/questions/12710582/how-can-i-capture-a-key-stroke-immediately-in-linux
648 */
649 char
xgetchar(void)650 xgetchar(void)
651 {
652 	struct termios oldt, newt;
653 	char c;
654 
655 	tcgetattr(STDIN_FILENO, &oldt);
656 	newt = oldt;
657 	newt.c_lflag &= (tcflag_t)~(ICANON | ECHO);
658 	tcsetattr(STDIN_FILENO, TCSANOW, &newt);
659 	c = (char)getchar();
660 	tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
661 
662 	return c;
663 }
664 
665 /* The following four functions (from_hex, to_hex, url_encode, and
666  * url_decode) were taken from "http://www.geekhideout.com/urlcode.shtml"
667  * and modified to comform to RFC 2395, as recommended by the
668  * freedesktop trash specification */
669 
670 /* Converts a hex char to its integer value */
671 char
from_hex(char c)672 from_hex(char c)
673 {
674 	return (char)(isdigit(c) ? c - '0' : tolower(c) - 'a' + 10);
675 }
676 
677 /* Converts an integer value to its hex form */
678 static char
to_hex(char c)679 to_hex(char c)
680 {
681 	static char hex[] = "0123456789ABCDEF";
682 	return hex[c & 15];
683 }
684 
685 /* Returns a url-encoded version of str */
686 char *
url_encode(char * str)687 url_encode(char *str)
688 {
689 	if (!str || !*str)
690 		return (char *)NULL;
691 
692 	char *buf = (char *)xnmalloc((strlen(str) * 3) + 1, sizeof(char));
693 	/* The max lenght of our buffer is 3 times the length of STR plus
694 	 * 1 extra byte for the null byte terminator: each char in STR will
695 	 * be, if encoded, %XX (3 chars) */
696 
697 	/* Copies of STR and BUF pointers to be able
698 	 * to increase and/or decrease them without loosing the original
699 	 * memory location */
700 	char *pstr, *pbuf;
701 	pstr = str;
702 	pbuf = buf;
703 
704 	for (; *pstr; pstr++) {
705 		if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.'
706 		|| *pstr == '~' || *pstr == '/') {
707 			/* Do not encode any of the above chars */
708 			*pbuf++ = *pstr;
709 		} else {
710 			/* Encode char to URL format. Example: space char to %20 */
711 			*pbuf++ = '%';
712 			*pbuf++ = to_hex(*pstr >> 4); /* Right shift operation */
713 			*pbuf++ = to_hex(*pstr & 15); /* Bitwise AND operation */
714 		}
715 	}
716 
717 	*pbuf = '\0';
718 	return buf;
719 }
720 
721 /* Returns a url-decoded version of str */
722 char *
url_decode(char * str)723 url_decode(char *str)
724 {
725 	if (!str || !*str)
726 		return (char *)NULL;
727 
728 	char *buf = (char *)xnmalloc(strlen(str) + 1, sizeof(char));
729 	/* The decoded string will be at most as long as the encoded
730 	 * string */
731 
732 	char *pstr, *pbuf;
733 	pstr = str;
734 	pbuf = buf;
735 	for (; *pstr; pstr++) {
736 		if (*pstr == '%') {
737 			if (pstr[1] && pstr[2]) {
738 				/* Decode URL code. Example: %20 to space char */
739 				/* Left shift and bitwise OR operations */
740 				*pbuf++ = (char)(from_hex(pstr[1]) << 4 | from_hex(pstr[2]));
741 				pstr += 2;
742 			}
743 		} else {
744 			*pbuf++ = *pstr;
745 		}
746 	}
747 
748 	*pbuf = '\0';
749 	return buf;
750 }
751 
752 /* Convert octal string into integer.
753  * Taken from: https://www.geeksforgeeks.org/program-octal-decimal-conversion/
754  * Used by decode_prompt() to make things like this work: \033[1;34m */
755 int
read_octal(char * str)756 read_octal(char *str)
757 {
758 	if (!str || !*str)
759 		return (-1);
760 
761 	int n = atoi(str);
762 	int num = n;
763 	int dec_value = 0;
764 
765 	/* Initializing base value to 1, i.e 8^0 */
766 	int base = 1;
767 
768 	int temp = num;
769 	while (temp) {
770 		/* Extracting last digit */
771 		int last_digit = temp % 10;
772 		temp = temp / 10;
773 
774 		/* Multiplying last digit with appropriate
775 		 * base value and adding it to dec_value */
776 		dec_value += last_digit * base;
777 		base = base * 8;
778 	}
779 
780 	return dec_value;
781 }
782