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