1 /*-------------------------------------------------------------------------
2  *
3  * infra.c
4  *	  a routines for build a infrastructure
5  *
6  * Portions Copyright (c) 2017-2021 Pavel Stehule
7  *
8  * IDENTIFICATION
9  *	  src/infra.c
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include <errno.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <time.h>
20 
21 #include "pspg.h"
22 #include "unicode.h"
23 
24 FILE	   *logfile = NULL;
25 
26 /*
27  * Print entry to log file
28  */
29 static void
print_log_prefix(void)30 print_log_prefix(void)
31 {
32 	time_t		rawtime;
33 	struct tm  *timeinfo;
34 	const char *asct;
35 	int		len;
36 
37 	time(&rawtime);
38 	timeinfo = localtime(&rawtime);
39 
40 	asct = asctime(timeinfo);
41 	len = strlen(asct);
42 
43 	fprintf(logfile, "%.*s ", len - 1, asct);
44 	fprintf(logfile, "[%ld] ", (long) getpid());
45 }
46 
47 void
log_row(const char * fmt,...)48 log_row(const char *fmt, ...)
49 {
50 	va_list		args;
51 
52 	if (logfile)
53 	{
54 		print_log_prefix();
55 
56 		va_start(args, fmt);
57 		vfprintf(logfile, fmt, args);
58 		va_end(args);
59 
60 		fputc('\n', logfile);
61 	}
62 
63 #ifdef DEBUG_PIPE
64 
65 	if (debug_pipe)
66 	{
67 		va_start(args, fmt);
68 		vfprintf(debug_pipe, fmt, args);
69 		va_end(args);
70 
71 		fputc('\n', debug_pipe);
72 	}
73 
74 #endif
75 
76 }
77 
78 void
leave(const char * fmt,...)79 leave(const char *fmt, ...)
80 {
81 	va_list		args;
82 
83 	/* close ncurses and input streams */
84 	exit_handler();
85 
86 	if (!fmt)
87 	{
88 		if (logfile)
89 		{
90 			fclose(logfile);
91 			logfile = NULL;
92 		}
93 
94 		exit(EXIT_FAILURE);
95 	}
96 
97 	va_start(args, fmt);
98 	vfprintf(stderr, fmt, args);
99 	va_end(args);
100 
101 	fputc('\n', stderr);
102 
103 	if (logfile)
104 	{
105 		print_log_prefix();
106 
107 		va_start(args, fmt);
108 		vfprintf(logfile, fmt, args);
109 		va_end(args);
110 
111 		fputc('\n', logfile);
112 
113 		fclose(logfile);
114 		logfile = NULL;
115 	}
116 
117 #ifdef DEBUG_PIPE
118 
119 	va_start(args, fmt);
120 	vfprintf(debug_pipe, fmt, args);
121 	va_end(args);
122 
123 	fputc('\n', debug_pipe);
124 
125 #endif
126 
127 	exit(EXIT_FAILURE);
128 }
129 
130 void
format_error(const char * fmt,...)131 format_error(const char *fmt, ...)
132 {
133 	va_list		args;
134 	char	   *ptr;
135 
136 	if (!current_state)
137 		leave("current_state is not initialized");
138 
139 	va_start(args, fmt);
140 	vsnprintf(pspg_errstr_buffer, PSPG_ERRSTR_BUFFER_SIZE, fmt, args);
141 	va_end(args);
142 
143 	current_state->errstr = pspg_errstr_buffer;
144 
145 	/* throw multilines strings */
146 	for (ptr = pspg_errstr_buffer; *ptr; ptr++)
147 	{
148 		if (*ptr == '\n')
149 		{
150 			*ptr = '\0';
151 			break;
152 		}
153 	}
154 }
155 
156 /*
157  * Safe memory operation.
158  */
159 void *
smalloc(int size)160 smalloc(int size)
161 {
162 	void	   *result;
163 
164 	result = malloc(size);
165 
166 	if (!result)
167 		leave("out of memory");
168 
169 	memset(result, 0, size);
170 
171 	return result;
172 }
173 
174 void *
srealloc(void * ptr,int size)175 srealloc(void *ptr, int size)
176 {
177 	void	   *result;
178 
179 	result = realloc(ptr, size);
180 
181 	if (!result)
182 		leave("out of memory");
183 
184 	return result;
185 }
186 
187 void *
smalloc2(int size,char * debugstr)188 smalloc2(int size, char *debugstr)
189 {
190 	void *result;
191 
192 	result = malloc(size);
193 	if (!result)
194 		leave("out of memory while %s", debugstr);
195 
196 	memset(result, 0, size);
197 
198 	return result;
199 }
200 
201 char *
sstrdup(const char * str)202 sstrdup(const char *str)
203 {
204 	char *result = strdup(str);
205 
206 	if (!result)
207 		leave("out of memory");
208 
209 	return result;
210 }
211 
212 char *
sstrdup2(const char * str,char * debugstr)213 sstrdup2(const char *str, char *debugstr)
214 {
215 	char *result = strdup(str);
216 
217 	if (!result)
218 		leave("out of memory while %s", debugstr);
219 
220 	return result;
221 }
222 
223 char *
sstrndup(const char * str,int bytes)224 sstrndup(const char *str, int bytes)
225 {
226 	char   *result, *ptr;
227 
228 	result = ptr = smalloc(bytes + 1);
229 
230 	while (*str && bytes-- > 0)
231 		*ptr++ = *str++;
232 
233 	*ptr = '\0';
234 
235 	return result;
236 }
237 
238 /*
239  * Returns byte size of first char of string
240  */
241 inline int
charlen(const char * str)242 charlen(const char *str)
243 {
244 	return use_utf8 ? utf8charlen(*str) : 1;
245 }
246 
247 inline int
dsplen(const char * str)248 dsplen(const char *str)
249 {
250 	return *str == ' ' ? 1 : (use_utf8 ? utf_dsplen(str) : 1);
251 }
252 
253 /*
254  * truncate spaces from both ends
255  */
256 char *
trim_str(const char * str,int * size)257 trim_str(const char *str, int *size)
258 {
259 	char   *result = NULL;
260 	int		bytes = *size;
261 
262 	while (*str == ' ' && bytes > 0)
263 	{
264 		str += 1;
265 		bytes -= 1;
266 	}
267 
268 	if (bytes > 0)
269 	{
270 		const char *after_nspc_chr = NULL;
271 
272 		result = (char *) str;
273 
274 		while (bytes > 0)
275 		{
276 			int		chrlen = charlen(str);
277 
278 			if (*str != ' ')
279 				after_nspc_chr = str + chrlen;
280 
281 			str = str + chrlen;
282 			bytes -= chrlen;
283 		}
284 
285 		*size = after_nspc_chr - result;
286 	}
287 	else
288 		*size = 0;
289 
290 	return result;
291 }
292 
293 /*
294  * truncate spaces from both ends, support quotes and double quotes
295  */
296 char *
trim_quoted_str(const char * str,int * size)297 trim_quoted_str(const char *str, int *size)
298 {
299 	char	   *result;
300 
301 	result = trim_str(str, size);
302 
303 	/* check first and last char */
304 	if (*size > 0)
305 	{
306 		if (*result == '"' || *result == '\'')
307 		{
308 			if (*result == *(result + *size - 1))
309 			{
310 				result += 1;
311 				*size -= 2;
312 			}
313 		}
314 	}
315 
316 	return result;
317 }
318 
319 /*
320  * Few simple functions for string concatetion
321  */
322 void
InitExtStr(ExtStr * estr)323 InitExtStr(ExtStr *estr)
324 {
325 	estr->len = 0;
326 	estr->maxlen = 1024;
327 	estr->data = smalloc(estr->maxlen);
328 	*estr->data = '\0';
329 }
330 
331 void
ResetExtStr(ExtStr * estr)332 ResetExtStr(ExtStr *estr)
333 {
334 	estr->len = 0;
335 
336 	/*
337 	 * Because the content self is still used, we should not to push
338 	 * ending zero there.
339 	 * DONT DO THIS *estr->data = '\0';
340 	 */
341 }
342 
343 void
ExtStrAppendNewLine(ExtStr * estr,char * str)344 ExtStrAppendNewLine(ExtStr *estr, char *str)
345 {
346 	int		size = strlen(str);
347 
348 	if (estr->len + size + 2 > estr->maxlen)
349 	{
350 		while (estr->len + size + 2 > estr->maxlen)
351 			estr->maxlen += 1024;
352 
353 		estr->data = srealloc(estr->data, estr->maxlen);
354 	}
355 
356 	if (estr->len > 0)
357 		estr->data[estr->len++] = '\n';
358 
359 	strncpy(&estr->data[estr->len], str, size + 1);
360 	estr->len += size;
361 }
362 
363 /*
364  * continuation_mark is related to new line symbol in text.
365  * continuation_mark is related to line break created by
366  * wrapped mode. continuation_mark2 is equal to previous
367  * continuous_mark
368  */
369 void
ExtStrAppendLine(ExtStr * estr,char * str,int size,char linestyle,bool continuation_mark,bool continuation_mark2)370 ExtStrAppendLine(ExtStr *estr,
371 				 char *str,
372 				 int size,
373 				 char linestyle,
374 				 bool continuation_mark,
375 				 bool continuation_mark2)
376 {
377 	bool	insert_nl = false;
378 
379 	str = trim_str(str, &size);
380 
381 	if (size == 0)
382 		return;
383 
384 	if (continuation_mark)
385 	{
386 		int		continuation_mark_size = 0;
387 		bool	wrapped_mode = false;
388 
389 		/* try to detect continuation marks at end of line */
390 		if (linestyle == 'a')
391 		{
392 			if (str[size - 1] == '+')
393 			{
394 				continuation_mark_size = 1;
395 				insert_nl = true;
396 			}
397 			else if (str[size - 1] == '.')
398 			{
399 				continuation_mark_size = 1;
400 				wrapped_mode = true;
401 			}
402 		}
403 		else
404 		{
405 			const char *u1 = "\342\206\265";	/* ↵ */
406 			const char *u2 = "\342\200\246";	/* … */
407 
408 			if (size > 3)
409 			{
410 				char	   *ptr = str + size - 3;
411 
412 				if (strncmp(ptr, u1, 3) == 0)
413 				{
414 					continuation_mark_size = 3;
415 					insert_nl = true;
416 				}
417 				else if (strncmp(ptr, u2, 3) == 0)
418 				{
419 					continuation_mark_size = 3;
420 					wrapped_mode = true;
421 				}
422 			}
423 		}
424 
425 		if (continuation_mark_size > 0)
426 		{
427 			size -= continuation_mark_size;
428 
429 			/*
430 			 * Trimming right end of string can eats spaces. In normal mode, it
431 			 * should not  be problem, because there is new line symbol, but in
432 			 * wrapped mode we can trim space that is used as word separator.
433 			 * So don't trim in wrapped mode.
434 			 */
435 			if (!wrapped_mode)
436 				str = trim_str(str, &size);
437 		}
438 	}
439 
440 	/*
441 	 * continuation mark can be on left side to (wrapped mode).
442 	 * We should to skip it.
443 	 */
444 	if (continuation_mark2)
445 	{
446 		int		cms = 0;		/* continuation mark size */
447 
448 		if (linestyle == 'a')
449 		{
450 			if (*str == '.')
451 				cms = 1;
452 		}
453 		else
454 		{
455 			const char *u1 = "\342\200\246";	/* … */
456 
457 			if (size > 3)
458 			{
459 				if (strncmp(str, u1, 3) == 0)
460 					cms = 3;
461 			}
462 		}
463 
464 		if (cms > 0)
465 		{
466 			str += cms;
467 			size -= cms;
468 		}
469 	}
470 
471 	if (estr->len + size + 2 > estr->maxlen)
472 	{
473 		while (estr->len + size + 2 > estr->maxlen)
474 			estr->maxlen += 1024;
475 
476 		estr->data = srealloc(estr->data, estr->maxlen);
477 	}
478 
479 	strncpy(&estr->data[estr->len], str, size);
480 	estr->len += size;
481 
482 	if (insert_nl)
483 		estr->data[estr->len++] = '\n';
484 
485 	estr->data[estr->len] = '\0';
486 }
487 
488 int
ExtStrTrimEnd(ExtStr * estr,bool replace_nl)489 ExtStrTrimEnd(ExtStr *estr, bool replace_nl)
490 {
491 	char	   *ptr;
492 	char	   *last_nonwhite = NULL;
493 
494 	ptr = estr->data;
495 
496 	while (*ptr)
497 	{
498 		if (*ptr != ' ' && *ptr != '\n')
499 			last_nonwhite = ptr;
500 
501 		if (*ptr == '\n' && replace_nl)
502 			*ptr = ' ';
503 
504 		ptr += charlen(ptr);
505 	}
506 
507 	if (last_nonwhite)
508 	{
509 		estr->len = last_nonwhite - estr->data + 1;
510 		estr->data[estr->len] = '\0';
511 	}
512 	else
513 		ResetExtStr(estr);
514 
515 	return estr->len;
516 }
517 
518 /*
519  * read write stderr poopen function
520  */
521 int
rwe_popen(char * command,int * fin,int * fout,int * ferr)522 rwe_popen(char *command, int *fin, int *fout, int *ferr)
523 {
524 	int		in[2];
525 	int		out[2];
526 	int		err[2];
527 	int		rc;
528 	int		saved_errno;
529 
530 	errno = 0;
531 	saved_errno = 0;
532 
533 	rc = pipe(in);
534 	if (rc == 0)
535 	{
536 		rc = pipe(out);
537 		if (rc == 0)
538 		{
539 			rc = pipe(err);
540 			if (rc == 0)
541 			{
542 				int		pid = fork();
543 
544 				if (pid > 0)
545 				{
546 					/* parent */
547 					close(in[0]);
548 					close(out[1]);
549 					close(err[1]);
550 
551 					*fin = in[1];
552 					*fout = out[0];
553 					*ferr = err[0];
554 
555 					return pid;
556 				}
557 				else if (pid == 0)
558 				{
559 					/* child */
560 					close(in[1]);
561 					close(out[0]);
562 					close(err[0]);
563 
564 					dup2(in[0], 0);
565 					dup2(out[1], 1);
566 					dup2(err[1], 2);
567 
568 					close(in[0]);
569 					close(out[1]);
570 					close(err[1]);
571 
572 					execl("/bin/sh", "sh", "-c", command, NULL);
573 					exit(127);
574 				}
575 				else
576 					saved_errno = errno;
577 
578 				close(err[0]);
579 				close(err[1]);
580 			}
581 			else
582 				saved_errno = errno;
583 
584 			close(out[0]);
585 			close(out[1]);
586 		}
587 		else
588 			saved_errno = errno;
589 
590 		close(in[0]);
591 		close(out[1]);
592 	}
593 	else
594 		saved_errno = errno;
595 
596 	errno = saved_errno;
597 
598 	return -1;
599 }
600 
601 /*
602  * Replace tilde by HOME dir
603  */
604 char *
tilde(char * dest,const char * path)605 tilde(char *dest, const char *path)
606 {
607 	static char buffer[MAXPATHLEN];
608 
609 	int			chars = 0;
610 	char	   *w;
611 
612 	if (!dest)
613 		dest = buffer;
614 
615 	w = dest;
616 
617 	while (*path && chars < MAXPATHLEN - 1)
618 	{
619 		if (*path == '~')
620 		{
621 			char *home = getenv("HOME");
622 
623 			if (home == NULL)
624 				leave("HOME directory is not defined");
625 
626 			while (*home && chars < MAXPATHLEN - 1)
627 			{
628 				*w++ = *home++;
629 				chars += 1;
630 			}
631 			path++;
632 		}
633 		else
634 		{
635 			*w++ = *path++;
636 			chars += 1;
637 		}
638 	}
639 
640 	*w = '\0';
641 
642 	return dest;
643 }
644