1 /*
2 
3     MPDM - Minimum Profit Data Manager
4     mpdm_f.c - File management
5 
6     ttcdt <dev@triptico.com> et al.
7 
8     This software is released into the public domain.
9     NO WARRANTY. See file LICENSE for details.
10 
11 */
12 
13 #include "config.h"
14 
15 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
16 #define _GNU_SOURCE
17 #endif
18 
19 #ifdef CONFOPT_WIN32
20 /* include this first to avoid clashing with standard select() */
21 #include <winsock2.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <wchar.h>
28 
29 #ifdef CONFOPT_WIN32
30 
31 #include <windows.h>
32 #include <commctrl.h>
33 #include <shlobj.h>
34 
35 #undef UNICODE
36 
37 #include <ws2tcpip.h>
38 
39 #else /* CONFOPT_WIN32 */
40 
41 #ifdef CONFOPT_GLOB_H
42 #include <glob.h>
43 #endif
44 
45 #ifdef CONFOPT_SYS_TYPES_H
46 #include <sys/types.h>
47 #endif
48 
49 #ifdef CONFOPT_SYS_WAIT_H
50 #include <sys/wait.h>
51 #endif
52 
53 #ifdef CONFOPT_SYS_SOCKET_H
54 #include <sys/socket.h>
55 #endif
56 
57 #ifdef CONFOPT_NETDB_H
58 #include <netdb.h>
59 #endif
60 
61 #include <fcntl.h>
62 
63 #endif /* CONFOPT_WIN32 */
64 
65 #ifdef CONFOPT_UNISTD_H
66 #include <unistd.h>
67 #endif
68 
69 #ifdef CONFOPT_PWD_H
70 #include <pwd.h>
71 #endif
72 
73 #ifdef CONFOPT_SYS_STAT_H
74 #include <sys/stat.h>
75 #endif
76 
77 #ifdef CONFOPT_SYS_FILE_H
78 #include <sys/file.h>
79 #endif
80 
81 #include <netinet/in.h>
82 
83 #include "mpdm.h"
84 
85 #ifdef CONFOPT_ICONV
86 #include <iconv.h>
87 #endif
88 
89 #define MAX_EOL 2
90 
91 /* file structure */
92 struct mpdm_file {
93     FILE *in;
94     FILE *out;
95 
96     int sock;
97     int is_pipe;
98     int skip_wait;
99 
100     wchar_t eol[MAX_EOL + 1];
101     int auto_chomp;
102 
103     wchar_t *(*f_read) (struct mpdm_file *, int *, int *);
104     int (*f_write)  (struct mpdm_file *, const wchar_t *);
105 
106 #ifdef CONFOPT_ICONV
107     iconv_t ic_enc;
108     iconv_t ic_dec;
109 #endif                          /* CONFOPT_ICONV */
110 
111 #ifdef CONFOPT_WIN32
112     HANDLE hin;
113     HANDLE hout;
114     HANDLE process;
115 #endif                          /* CONFOPT_WIN32 */
116 };
117 
118 #include <errno.h>
119 
120 
121 /** code **/
122 
123 
store_syserr(void)124 static void store_syserr(void)
125 /* stores the system error inside the global ERRNO */
126 {
127     mpdm_set_wcs(mpdm_root(), MPDM_MBS(strerror(errno)), L"ERRNO");
128 }
129 
130 
get_byte(struct mpdm_file * f)131 static int get_byte(struct mpdm_file *f)
132 /* reads a byte from a file structure */
133 {
134     int c = EOF;
135 
136 #ifdef CONFOPT_WIN32
137 
138     if (f->hin != NULL) {
139         char tmp;
140         DWORD n;
141 
142         if (ReadFile(f->hin, &tmp, 1, &n, NULL) && n > 0)
143             c = (int) tmp;
144     }
145 
146 #endif /* CONFOPT_WIN32 */
147 
148     if (f->in != NULL) {
149         /* read (converting to positive if needed) */
150         if ((c = fgetc(f->in)) < 0 && !feof(f->in))
151             c += 256;
152     }
153 
154     if (f->sock != -1) {
155         char b;
156 
157         if (recv(f->sock, &b, sizeof(b), 0) == sizeof(b))
158             c = b;
159     }
160 
161     return c;
162 }
163 
164 
put_buf(const char * ptr,int s,struct mpdm_file * f)165 static int put_buf(const char *ptr, int s, struct mpdm_file *f)
166 /* writes s bytes in the buffer in ptr to f */
167 {
168 #ifdef CONFOPT_WIN32
169 
170     if (f->hout != NULL) {
171         DWORD n;
172 
173         if (WriteFile(f->hout, ptr, s, &n, NULL) && n > 0)
174             s = n;
175     }
176     else
177 #endif                          /* CONFOPT_WIN32 */
178 
179     if (f->out != NULL)
180         s = fwrite(ptr, s, 1, f->out);
181 
182     if (f->sock != -1)
183         s = send(f->sock, ptr, s, 0);
184 
185     return s;
186 }
187 
188 
put_char(int c,struct mpdm_file * f)189 static int put_char(int c, struct mpdm_file *f)
190 /* writes a character in a file structure */
191 {
192     char tmp = c;
193 
194     if (put_buf(&tmp, 1, f) != 1)
195         c = EOF;
196 
197     return c;
198 }
199 
200 
store_in_line(wchar_t ** ptr,int * s,int * eol,wchar_t wc)201 static int store_in_line(wchar_t **ptr, int *s, int *eol, wchar_t wc)
202 /* store the c in the line, keeping track for EOLs */
203 {
204     int done = 0;
205 
206     /* track EOL sequence position */
207     if (eol && *eol == -1) {
208         if (wc == L'\r' || wc == L'\n')
209             *eol = *s;
210     }
211 
212     /* store */
213     *ptr = mpdm_pokewsn(*ptr, s, &wc, 1);
214 
215     /* end of line? finish */
216     if (wc == L'\n')
217         done = 1;
218 
219     return done;
220 }
221 
222 
read_mbs(struct mpdm_file * f,int * s,int * eol)223 static wchar_t *read_mbs(struct mpdm_file *f, int *s, int *eol)
224 /* reads a multibyte string from a mpdm_file into a dynamic string */
225 {
226     char tmp[128];
227     wchar_t *ptr = NULL;
228     int c, i = 0;
229     wchar_t wc;
230     mbstate_t ps;
231 
232     while ((c = get_byte(f)) != EOF) {
233         int r = -1;
234 
235         if (i < sizeof(tmp)) {
236             tmp[i++] = c;
237 
238             /* try to convert what is read by now */
239             memset(&ps, '\0', sizeof(ps));
240             r = mbrtowc(&wc, tmp, i, &ps);
241 
242             /* incomplete sequence? keep trying */
243             if (r == -2)
244                 continue;
245         }
246 
247         if (r == -1) {
248             /* too many failing bytes or invalid sequence;
249                use the Unicode replacement char */
250             wc = L'\xfffd';
251         }
252 
253         i = 0;
254 
255         if (store_in_line(&ptr, s, eol, wc))
256             break;
257     }
258 
259     return ptr;
260 }
261 
262 
write_wcs(struct mpdm_file * f,const wchar_t * str)263 static int write_wcs(struct mpdm_file *f, const wchar_t *str)
264 /* writes a wide string to an struct mpdm_file */
265 {
266     int s;
267     char *ptr;
268 
269     ptr = mpdm_wcstombs(str, &s);
270     s = put_buf(ptr, s, f);
271     free(ptr);
272 
273     return s;
274 }
275 
276 
277 #ifdef CONFOPT_ICONV
278 
read_iconv(struct mpdm_file * f,int * s,int * eol)279 static wchar_t *read_iconv(struct mpdm_file *f, int *s, int *eol)
280 /* reads a multibyte string transforming with iconv */
281 {
282     char tmp[128];
283     wchar_t *ptr = NULL;
284     int c, i = 0;
285     wchar_t wc;
286 
287     /* resets the decoder */
288     iconv(f->ic_dec, NULL, NULL, NULL, NULL);
289 
290     while ((c = get_byte(f)) != EOF) {
291         size_t il, ol;
292         char *iptr, *optr;
293 
294         tmp[i++] = c;
295 
296         /* too big? shouldn't happen */
297         if (i == sizeof(tmp))
298             break;
299 
300         il = i;
301         iptr = tmp;
302         ol = sizeof(wchar_t);
303         optr = (char *) &wc;
304 
305         /* write to file */
306         if (iconv(f->ic_dec, &iptr, &il, &optr, &ol) == (size_t) - 1) {
307             /* found incomplete multibyte character */
308             if (errno == EINVAL)
309                 continue;
310 
311             /* otherwise, return '?' */
312             wc = L'\xfffd';
313         }
314 
315         i = 0;
316 
317         if (store_in_line(&ptr, s, eol, wc))
318             break;
319     }
320 
321     return ptr;
322 }
323 
324 
write_iconv(struct mpdm_file * f,const wchar_t * str)325 static int write_iconv(struct mpdm_file *f, const wchar_t *str)
326 /* writes a wide string to a stream using iconv */
327 {
328     char tmp[128];
329     int cnt = 0;
330 
331     /* resets the encoder */
332     iconv(f->ic_enc, NULL, NULL, NULL, NULL);
333 
334     /* convert char by char */
335     for (; *str != L'\0'; str++) {
336         size_t il, ol;
337         char *iptr, *optr;
338         int n;
339 
340         il = sizeof(wchar_t);
341         iptr = (char *) str;
342         ol = sizeof(tmp);
343         optr = tmp;
344 
345         /* write to file */
346         if (iconv(f->ic_enc, &iptr, &il, &optr, &ol) == (size_t) - 1) {
347             /* error converting; convert a '?' instead */
348             wchar_t q = L'?';
349 
350             il = sizeof(wchar_t);
351             iptr = (char *) &q;
352             ol = sizeof(tmp);
353             optr = tmp;
354 
355             iconv(f->ic_enc, &iptr, &il, &optr, &ol);
356         }
357 
358         for (n = 0; n < (int) (sizeof(tmp) - ol); n++, cnt++) {
359             if (put_char(tmp[n], f) == EOF)
360                 return -1;
361         }
362     }
363 
364     return cnt;
365 }
366 
367 
368 #endif /* CONFOPT_ICONV */
369 
370 #define BYTE_OR_BREAK(c, f) if((c = get_byte(f)) == EOF) break
371 
read_utf8(struct mpdm_file * f,int * s,int * eol)372 static wchar_t *read_utf8(struct mpdm_file *f, int *s, int *eol)
373 /* utf8 reader */
374 {
375     wchar_t *ptr = NULL;
376     int c, st = 0;
377 
378     while ((c = get_byte(f)) != EOF) {
379         wchar_t wc;
380 
381         if (mpdm_utf8_to_wc(&wc, &st, c) == 0)
382             if (store_in_line(&ptr, s, eol, wc))
383                 break;
384     }
385 
386     return ptr;
387 }
388 
389 
write_utf8(struct mpdm_file * f,const wchar_t * str)390 static int write_utf8(struct mpdm_file *f, const wchar_t *str)
391 /* utf8 writer */
392 {
393     int cnt = 0;
394     wchar_t wc;
395 
396     /* convert char by char */
397     for (; (wc = *str) != L'\0'; str++) {
398         if (wc < 0x80)
399             put_char((int) wc, f);
400         else
401         if (wc < 0x800) {
402             put_char((int) (0xc0 | (wc >> 6)), f);
403             put_char((int) (0x80 | (wc & 0x3f)), f);
404             cnt++;
405         }
406         else
407         if (wc < 0x10000) {
408             put_char((int) (0xe0 | (wc >> 12)), f);
409             put_char((int) (0x80 | ((wc >> 6) & 0x3f)), f);
410             put_char((int) (0x80 | (wc & 0x3f)), f);
411             cnt += 2;
412         }
413         else
414         if (wc < 0x200000) {
415             put_char((int) (0xf0 | (wc >> 18)), f);
416             put_char((int) (0x80 | ((wc >> 12) & 0x3f)), f);
417             put_char((int) (0x80 | ((wc >> 6) & 0x3f)), f);
418             put_char((int) (0x80 | (wc & 0x3f)), f);
419             cnt += 3;
420         }
421 
422         cnt++;
423     }
424 
425     return cnt;
426 }
427 
428 
read_utf8_bom(struct mpdm_file * f,int * s,int * eol)429 static wchar_t *read_utf8_bom(struct mpdm_file *f, int *s, int *eol)
430 /* utf-8 reader with BOM detection */
431 {
432     wchar_t *enc = L"";
433 
434     f->f_read = NULL;
435 
436     /* autodetection */
437     if (get_byte(f) == 0xef && get_byte(f) == 0xbb && get_byte(f) == 0xbf)
438         enc = L"utf-8bom";
439     else {
440         enc = L"utf-8";
441         fseek(f->in, 0, SEEK_SET);
442     }
443 
444     mpdm_set_wcs(mpdm_root(), MPDM_S(enc), L"DETECTED_ENCODING");
445 
446     /* we're utf-8 from now on */
447     f->f_read = read_utf8;
448 
449     return f->f_read(f, s, eol);
450 }
451 
452 
write_utf8_bom(struct mpdm_file * f,const wchar_t * str)453 static int write_utf8_bom(struct mpdm_file *f, const wchar_t *str)
454 /* utf-8 writer with BOM */
455 {
456     /* store the BOM */
457     put_char(0xef, f);
458     put_char(0xbb, f);
459     put_char(0xbf, f);
460 
461     /* we're utf-8 from now on */
462     f->f_write = write_utf8;
463 
464     return f->f_write(f, str);
465 }
466 
467 
read_iso8859_1(struct mpdm_file * f,int * s,int * eol)468 static wchar_t *read_iso8859_1(struct mpdm_file *f, int *s, int *eol)
469 /* iso8859-1 reader */
470 {
471     wchar_t *ptr = NULL;
472     wchar_t wc;
473     int c;
474 
475     while ((c = get_byte(f)) != EOF) {
476         wc = c;
477 
478         if (store_in_line(&ptr, s, eol, wc))
479             break;
480     }
481 
482     return ptr;
483 }
484 
485 
write_iso8859_1(struct mpdm_file * f,const wchar_t * str)486 static int write_iso8859_1(struct mpdm_file *f, const wchar_t *str)
487 /* iso8859-1 writer */
488 {
489     int cnt = 0;
490     wchar_t wc;
491 
492     /* convert char by char */
493     for (; (wc = *str) != L'\0'; str++)
494         put_char(wc <= 0xff ? (int) wc : '?', f);
495 
496     return cnt;
497 }
498 
499 
read_utf16ae(struct mpdm_file * f,int * s,int * eol,int le)500 static wchar_t *read_utf16ae(struct mpdm_file *f, int *s, int *eol, int le)
501 /* utf16 reader, ANY ending */
502 {
503     wchar_t *ptr = NULL;
504     wchar_t wc;
505     int c1, c2;
506 
507     for (;;) {
508         wc = L'\0';
509 
510         BYTE_OR_BREAK(c1, f);
511         BYTE_OR_BREAK(c2, f);
512 
513         if (le)
514             wc = c1 | (c2 << 8);
515         else
516             wc = c2 | (c1 << 8);
517 
518         if (store_in_line(&ptr, s, eol, wc))
519             break;
520     }
521 
522     return ptr;
523 }
524 
525 
write_utf16ae(struct mpdm_file * f,const wchar_t * str,int le)526 static int write_utf16ae(struct mpdm_file *f, const wchar_t *str, int le)
527 /* utf16 writer, ANY ending */
528 {
529     int cnt = 0;
530     wchar_t wc;
531 
532     /* convert char by char */
533     for (; (wc = *str) != L'\0'; str++) {
534 
535         if (le) {
536             put_char(wc & 0xff, f);
537             put_char((wc & 0xff00) >> 8, f);
538         }
539         else {
540             put_char((wc & 0xff00) >> 8, f);
541             put_char(wc & 0xff, f);
542         }
543     }
544 
545     return cnt;
546 }
547 
548 
read_utf16le(struct mpdm_file * f,int * s,int * eol)549 static wchar_t *read_utf16le(struct mpdm_file *f, int *s, int *eol)
550 {
551     return read_utf16ae(f, s, eol, 1);
552 }
553 
554 
write_utf16le(struct mpdm_file * f,const wchar_t * str)555 static int write_utf16le(struct mpdm_file *f, const wchar_t *str)
556 {
557     return write_utf16ae(f, str, 1);
558 }
559 
560 
read_utf16be(struct mpdm_file * f,int * s,int * eol)561 static wchar_t *read_utf16be(struct mpdm_file *f, int *s, int *eol)
562 {
563     return read_utf16ae(f, s, eol, 0);
564 }
565 
566 
write_utf16be(struct mpdm_file * f,const wchar_t * str)567 static int write_utf16be(struct mpdm_file *f, const wchar_t *str)
568 {
569     return write_utf16ae(f, str, 0);
570 }
571 
572 
read_utf16(struct mpdm_file * f,int * s,int * eol)573 static wchar_t *read_utf16(struct mpdm_file *f, int *s, int *eol)
574 {
575     int c1, c2;
576     wchar_t *enc = L"utf-16le";
577 
578     /* assume little-endian */
579     f->f_read = read_utf16le;
580 
581     /* autodetection */
582     c1 = get_byte(f);
583     c2 = get_byte(f);
584 
585     if (c1 == 0xfe && c2 == 0xff) {
586         enc = L"utf-16be";
587         f->f_read = read_utf16be;
588     }
589     else
590     if (c1 != 0xff || c2 != 0xfe) {
591         /* no BOM; rewind and hope */
592         fseek(f->in, 0, SEEK_SET);
593     }
594 
595     mpdm_set_wcs(mpdm_root(), MPDM_S(enc), L"DETECTED_ENCODING");
596 
597     return f->f_read(f, s, eol);
598 }
599 
600 
write_utf16le_bom(struct mpdm_file * f,const wchar_t * str)601 static int write_utf16le_bom(struct mpdm_file *f, const wchar_t *str)
602 {
603     /* store the LE signature */
604     put_char(0xff, f);
605     put_char(0xfe, f);
606 
607     /* we're 16le from now on */
608     f->f_write = write_utf16le;
609 
610     return f->f_write(f, str);
611 }
612 
613 
write_utf16be_bom(struct mpdm_file * f,const wchar_t * str)614 static int write_utf16be_bom(struct mpdm_file *f, const wchar_t *str)
615 {
616     /* store the BE signature */
617     put_char(0xfe, f);
618     put_char(0xff, f);
619 
620     /* we're 16be from now on */
621     f->f_write = write_utf16be;
622 
623     return f->f_write(f, str);
624 }
625 
626 
read_utf32ae(struct mpdm_file * f,int * s,int * eol,int le)627 static wchar_t *read_utf32ae(struct mpdm_file *f, int *s, int *eol, int le)
628 /* utf32 reader, ANY ending */
629 {
630     wchar_t *ptr = NULL;
631     wchar_t wc;
632     int c1, c2, c3, c4;
633 
634     for (;;) {
635         wc = L'\0';
636 
637         BYTE_OR_BREAK(c1, f);
638         BYTE_OR_BREAK(c2, f);
639         BYTE_OR_BREAK(c3, f);
640         BYTE_OR_BREAK(c4, f);
641 
642         if (le)
643             wc = c1 | (c2 << 8) | (c3 << 16) | (c4 << 24);
644         else
645             wc = c4 | (c3 << 8) | (c2 << 16) | (c1 << 24);
646 
647         if (store_in_line(&ptr, s, eol, wc))
648             break;
649     }
650 
651     return ptr;
652 }
653 
654 
write_utf32ae(struct mpdm_file * f,const wchar_t * str,int le)655 static int write_utf32ae(struct mpdm_file *f, const wchar_t *str, int le)
656 /* utf32 writer, ANY ending */
657 {
658     int cnt = 0;
659     wchar_t wc;
660 
661     /* convert char by char */
662     for (; (wc = *str) != L'\0'; str++) {
663 
664         if (le) {
665             put_char((wc & 0x000000ff), f);
666             put_char((wc & 0x0000ff00) >> 8, f);
667             put_char((wc & 0x00ff0000) >> 16, f);
668             put_char((wc & 0xff000000) >> 24, f);
669         }
670         else {
671             put_char((wc & 0xff000000) >> 24, f);
672             put_char((wc & 0x00ff0000) >> 16, f);
673             put_char((wc & 0x0000ff00) >> 8, f);
674             put_char((wc & 0x000000ff), f);
675         }
676     }
677 
678     return cnt;
679 }
680 
681 
read_utf32le(struct mpdm_file * f,int * s,int * eol)682 static wchar_t *read_utf32le(struct mpdm_file *f, int *s, int *eol)
683 {
684     return read_utf32ae(f, s, eol, 1);
685 }
686 
687 
write_utf32le(struct mpdm_file * f,const wchar_t * str)688 static int write_utf32le(struct mpdm_file *f, const wchar_t *str)
689 {
690     return write_utf32ae(f, str, 1);
691 }
692 
693 
read_utf32be(struct mpdm_file * f,int * s,int * eol)694 static wchar_t *read_utf32be(struct mpdm_file *f, int *s, int *eol)
695 {
696     return read_utf32ae(f, s, eol, 0);
697 }
698 
699 
write_utf32be(struct mpdm_file * f,const wchar_t * str)700 static int write_utf32be(struct mpdm_file *f, const wchar_t *str)
701 {
702     return write_utf32ae(f, str, 0);
703 }
704 
705 
read_utf32(struct mpdm_file * f,int * s,int * eol)706 static wchar_t *read_utf32(struct mpdm_file *f, int *s, int *eol)
707 {
708     int c1, c2, c3, c4;
709     wchar_t *enc = L"utf-32le";
710 
711     f->f_read = read_utf32le;
712 
713     /* autodetection */
714     c1 = get_byte(f);
715     c2 = get_byte(f);
716     c3 = get_byte(f);
717     c4 = get_byte(f);
718 
719     if (c1 == 0 && c2 == 0 && c3 == 0xfe && c4 == 0xff) {
720         enc = L"utf-32be";
721         f->f_read = read_utf32be;
722     }
723     if (c1 != 0xff || c2 != 0xfe || c3 != 0 || c4 != 0) {
724         /* no BOM; assume le and hope */
725         fseek(f->in, 0, SEEK_SET);
726     }
727 
728     mpdm_set_wcs(mpdm_root(), MPDM_S(enc), L"DETECTED_ENCODING");
729 
730     return f->f_read(f, s, eol);
731 }
732 
733 
write_utf32le_bom(struct mpdm_file * f,const wchar_t * str)734 static int write_utf32le_bom(struct mpdm_file *f, const wchar_t *str)
735 {
736     /* store the LE signature */
737     put_char(0xff, f);
738     put_char(0xfe, f);
739     put_char(0, f);
740     put_char(0, f);
741 
742     /* we're 32le from now on */
743     f->f_write = write_utf32le;
744 
745     return f->f_write(f, str);
746 }
747 
748 
write_utf32be_bom(struct mpdm_file * f,const wchar_t * str)749 static int write_utf32be_bom(struct mpdm_file *f, const wchar_t *str)
750 {
751     /* store the BE signature */
752     put_char(0, f);
753     put_char(0, f);
754     put_char(0xfe, f);
755     put_char(0xff, f);
756 
757     /* we're 32be from now on */
758     f->f_write = write_utf32be;
759 
760     return f->f_write(f, str);
761 }
762 
763 
764 static wchar_t *msdos_437 = L"\x2302"
765     "\x00C7\x00FC\x00E9\x00E2\x00E4\x00E0\x00E5\x00E7"
766     "\x00EA\x00EB\x00E8\x00EF\x00EE\x00EC\x00C4\x00C5"
767     "\x00C9\x00E6\x00C6\x00F4\x00F6\x00F2\x00FB\x00F9"
768     "\x00FF\x00D6\x00DC\x00A2\x00A3\x00A5\x20A7\x0192"
769     "\x00E1\x00ED\x00F3\x00FA\x00F1\x00D1\x00AA\x00BA"
770     "\x00BF\x2310\x00AC\x00BD\x00BC\x00A1\x00AB\x00BB"
771     "\x2591\x2592\x2593\x2502\x2524\x2561\x2562\x2556"
772     "\x2555\x2563\x2551\x2557\x255D\x255C\x255B\x2510"
773     "\x2514\x2534\x252C\x251C\x2500\x253C\x255E\x255F"
774     "\x255A\x2554\x2569\x2566\x2560\x2550\x256C\x2567"
775     "\x2568\x2564\x2565\x2559\x2558\x2552\x2553\x256B"
776     "\x256A\x2518\x250C\x2588\x2584\x258C\x2590\x2580"
777     "\x03B1\x03B2\x0393\x03C0\x03A3\x03C3\x00B5\x03C4"
778     "\x03A6\x0398\x03A9\x03B4\x221E\x2205\x2208\x2229"
779     "\x2261\x00B1\x2265\x2264\x2320\x2321\x00F7\x2248"
780     "\x00B0\x2219\x00B7\x221A\x207F\x00B2\x25A0\x00A0";
781 
782 static wchar_t *msdos_850 = L"\x2302"
783     "\x00C7\x00FC\x00E9\x00E2\x00E4\x00E0\x00E5\x00E7"
784     "\x00EA\x00EB\x00E8\x00EF\x00EE\x00EC\x00C4\x00C5"
785     "\x00C9\x00E6\x00C6\x00F4\x00F6\x00F2\x00FB\x00F9"
786     "\x00FF\x00D6\x00DC\x00F8\x00A3\x00D8\x00D7\x0192"
787     "\x00E1\x00ED\x00F3\x00FA\x00F1\x00D1\x00AA\x00BA"
788     "\x00BF\x00AE\x00AC\x00BD\x00BC\x00A1\x00AB\x00BB"
789     "\x2591\x2592\x2593\x2502\x2524\x00C1\x00C2\x00C0"
790     "\x00A9\x2563\x2551\x2557\x255D\x00A2\x00A5\x2510"
791     "\x2514\x2534\x252C\x251C\x2500\x253C\x00E3\x00C3"
792     "\x255A\x2554\x2569\x2566\x2560\x2550\x256C\x00A4"
793     "\x00F0\x00D0\x00CA\x00CB\x00C8\x0131\x00CD\x00CE"
794     "\x00CF\x2518\x250C\x2588\x2584\x00A6\x00CC\x2580"
795     "\x00D3\x00DF\x00D4\x00D2\x00F5\x00D5\x00B5\x00FE"
796     "\x00DE\x00DA\x00DB\x00D9\x00FD\x00DD\x00AF\x00B4"
797     "\x00AD\x00B1\x2017\x00BE\x00B6\x00A7\x00F7\x00B8"
798     "\x00B0\x00A8\x00B7\x00B9\x00B3\x00B2\x25A0\x00A0";
799 
800 static wchar_t *windows_1252 = L"\x2302"
801     "\x20AC\x0081\x201A\x0192\x201E\x2026\x2020\x2021"
802     "\x02C6\x2030\x0160\x2039\x0152\x008D\x017D\x008F"
803     "\x0090\x2018\x2019\x201C\x201D\x2022\x2013\x2014"
804     "\x02DC\x2122\x0161\x203A\x0153\x009D\x017E\x0178"
805     "\x00A0\x00A1\x00A2\x00A3\x00A4\x00A5\x00A6\x00A7"
806     "\x00A8\x00A9\x00AA\x00AB\x00AC\x00AD\x00AE\x00AF"
807     "\x00B0\x00B1\x00B2\x00B3\x00B4\x00B5\x00B6\x00B7"
808     "\x00B8\x00B9\x00BA\x00BB\x00BC\x00BD\x00BE\x00BF"
809     "\x00C0\x00C1\x00C2\x00C3\x00C4\x00C5\x00C6\x00C7"
810     "\x00C8\x00C9\x00CA\x00CB\x00CC\x00CD\x00CE\x00CF"
811     "\x00D0\x00D1\x00D2\x00D3\x00D4\x00D5\x00D6\x00D7"
812     "\x00D8\x00D9\x00DA\x00DB\x00DC\x00DD\x00DE\x00DF"
813     "\x00E0\x00E1\x00E2\x00E3\x00E4\x00E5\x00E6\x00E7"
814     "\x00E8\x00E9\x00EA\x00EB\x00EC\x00ED\x00EE\x00EF"
815     "\x00F0\x00F1\x00F2\x00F3\x00F4\x00F5\x00F6\x00F7"
816     "\x00F8\x00F9\x00FA\x00FB\x00FC\x00FD\x00FE\x00FF";
817 
read_msdos(struct mpdm_file * f,int * s,int * eol,wchar_t * cp)818 static wchar_t *read_msdos(struct mpdm_file *f, int *s, int *eol, wchar_t *cp)
819 /* generic MSDOS reader */
820 {
821     wchar_t *ptr = NULL;
822     wchar_t wc;
823     int c;
824 
825     while ((c = get_byte(f)) != EOF) {
826 
827         if (c > 127)
828             wc = cp[c - 127];
829         else
830             wc = c;
831 
832         if (store_in_line(&ptr, s, eol, wc))
833             break;
834     }
835 
836     return ptr;
837 }
838 
839 
write_msdos(struct mpdm_file * f,const wchar_t * str,wchar_t * cp)840 static int write_msdos(struct mpdm_file *f, const wchar_t *str, wchar_t *cp)
841 /* generic MSDOS writer */
842 {
843     int cnt = 0;
844     wchar_t wc;
845 
846     /* convert char by char */
847     for (; (wc = *str) != L'\0'; str++) {
848         int c = wc;
849 
850         if (c > 0xff)
851             c = '?';
852         else
853         if (c >= 127) {
854             int n;
855 
856             c = '?';
857             for (n = 0; c == '?' && cp[n]; n++) {
858                 if (wc == cp[n])
859                     c = 127 + n;
860             }
861         }
862 
863         put_char(c, f);
864     }
865 
866     return cnt;
867 }
868 
869 
read_msdos_437(struct mpdm_file * f,int * s,int * eol)870 static wchar_t *read_msdos_437(struct mpdm_file *f, int *s, int *eol)
871 {
872     return read_msdos(f, s, eol, msdos_437);
873 }
874 
875 
write_msdos_437(struct mpdm_file * f,const wchar_t * str)876 static int write_msdos_437(struct mpdm_file *f, const wchar_t *str)
877 {
878     return write_msdos(f, str, msdos_437);
879 }
880 
881 
read_msdos_850(struct mpdm_file * f,int * s,int * eol)882 static wchar_t *read_msdos_850(struct mpdm_file *f, int *s, int *eol)
883 {
884     return read_msdos(f, s, eol, msdos_850);
885 }
886 
887 
write_msdos_850(struct mpdm_file * f,const wchar_t * str)888 static int write_msdos_850(struct mpdm_file *f, const wchar_t *str)
889 {
890     return write_msdos(f, str, msdos_850);
891 }
892 
893 
read_windows_1252(struct mpdm_file * f,int * s,int * eol)894 static wchar_t *read_windows_1252(struct mpdm_file *f, int *s, int *eol)
895 {
896     return read_msdos(f, s, eol, windows_1252);
897 }
898 
899 
write_windows_1252(struct mpdm_file * f,const wchar_t * str)900 static int write_windows_1252(struct mpdm_file *f, const wchar_t *str)
901 {
902     return write_msdos(f, str, windows_1252);
903 }
904 
905 
read_auto(struct mpdm_file * f,int * s,int * eol)906 static wchar_t *read_auto(struct mpdm_file *f, int *s, int *eol)
907 /* autodetects different encodings based on the BOM */
908 {
909     wchar_t *enc = L"";
910 
911     /* by default, multibyte reading */
912     f->f_read = read_mbs;
913 
914     /* ensure seeking is possible */
915     if (f->in != NULL && fseek(f->in, 0, SEEK_CUR) != -1) {
916         int c;
917 
918         c = get_byte(f);
919 
920         if (c == 0xff) {
921             /* can be utf32le or utf16le */
922             if (get_byte(f) == 0xfe) {
923                 /* if next 2 chars are 0x00, it's utf32; otherwise utf16 */
924                 if (get_byte(f) == 0x00 && get_byte(f) == 0x00) {
925                     enc = L"utf-32le";
926                     f->f_read = read_utf32le;
927                     goto got_encoding;
928                 }
929                 else {
930                     /* rewind to 3rd character */
931                     fseek(f->in, 2, SEEK_SET);
932 
933                     enc = L"utf-16le";
934                     f->f_read = read_utf16le;
935                     goto got_encoding;
936                 }
937             }
938         }
939         else
940         if (c == 0x00) {
941             /* can be utf32be */
942             if (get_byte(f) == 0x00 && get_byte(f) == 0xfe
943                 && get_byte(f) == 0xff) {
944                 enc = L"utf-32be";
945                 f->f_read = read_utf32be;
946                 goto got_encoding;
947             }
948         }
949         else
950         if (c == 0xfe) {
951             /* can be utf16be */
952             if (get_byte(f) == 0xff) {
953                 enc = L"utf-16be";
954                 f->f_read = read_utf16be;
955                 goto got_encoding;
956             }
957         }
958         else
959         if (c == 0xef) {
960             /* can be utf8 with BOM */
961             if (get_byte(f) == 0xbb && get_byte(f) == 0xbf) {
962                 enc = L"utf-8bom";
963                 f->f_read = read_utf8;
964                 goto got_encoding;
965             }
966         }
967         else {
968             /* try if a first bunch of chars are valid UTF-8 */
969             int p = c;
970             int n = 10000;
971             int u = 0;
972 
973             while (--n && (c = get_byte(f)) != EOF) {
974                 if ((c & 0xc0) == 0x80) {
975                     if ((p & 0xc0) == 0xc0)
976                         u++;
977                     else
978                     if ((p & 0x80) == 0x00) {
979                         u = -1;
980                         break;
981                     }
982                 }
983                 else
984                 if ((p & 0xc0) == 0xc0) {
985                     u = -1;
986                     break;
987                 }
988 
989                 p = c;
990             }
991 
992             if (u < 0) {
993                 /* invalid utf-8; fall back to 8bit */
994                 enc = L"8bit";
995                 f->f_read = read_iso8859_1;
996             }
997             else
998             if (u > 0) {
999                 /* utf-8 sequences found */
1000                 enc = L"utf-8";
1001                 f->f_read = read_utf8;
1002             }
1003 
1004             /* 7 bit ASCII: do nothing */
1005         }
1006 
1007         /* none of the above; restart */
1008         fseek(f->in, 0, SEEK_SET);
1009     }
1010 
1011 got_encoding:
1012     mpdm_set_wcs(mpdm_root(), MPDM_S(enc), L"DETECTED_ENCODING");
1013 
1014     return f->f_read(f, s, eol);
1015 }
1016 
1017 
1018 /** interface **/
1019 
mpdm_read_mbs(FILE * f,int * s)1020 wchar_t *mpdm_read_mbs(FILE *f, int *s)
1021 /* reads a multibyte string from a stream into a dynamic string */
1022 {
1023     struct mpdm_file fs;
1024 
1025     /* reset the structure */
1026     memset(&fs, '\0', sizeof(fs));
1027     fs.in = f;
1028 
1029     *s = 0;
1030     return read_mbs(&fs, s, NULL);
1031 }
1032 
1033 
mpdm_write_wcs(FILE * f,const wchar_t * str)1034 int mpdm_write_wcs(FILE *f, const wchar_t *str)
1035 /* writes a wide string to a stream */
1036 {
1037     struct mpdm_file fs;
1038 
1039     /* reset the structure */
1040     memset(&fs, '\0', sizeof(fs));
1041     fs.out = f;
1042 
1043     return write_wcs(&fs, str);
1044 }
1045 
1046 
1047 /**
1048  * mpdm_open - Opens a file.
1049  * @filename: the file name
1050  * @mode: an fopen-like mode string
1051  *
1052  * Opens a file. If @filename can be open in the specified @mode, an
1053  * mpdm_t value will be returned containing the file descriptor, or NULL
1054  * otherwise. If @mode is NULL, "r" is assumed.
1055  *
1056  * If the file is open for reading, some charset detection methods are
1057  * used. If any of them is successful, its name is stored in the
1058  * DETECTED_ENCODING element of the mpdm_root() hash. This value is
1059  * suitable to be copied over ENCODING or TEMP_ENCODING.
1060  *
1061  * If the file is open for writing, the encoding to be used is read from
1062  * the ENCODING element of mpdm_root() and, if not set, from the
1063  * TEMP_ENCODING one. The latter will always be deleted afterwards.
1064  * [File Management]
1065  */
mpdm_open(mpdm_t filename,mpdm_t mode)1066 mpdm_t mpdm_open(mpdm_t filename, mpdm_t mode)
1067 {
1068     FILE *f = NULL;
1069     mpdm_t fn;
1070     mpdm_t fm;
1071 
1072     /* extreme lazyness */
1073     if (mode == NULL)
1074         mode = MPDM_S(L"r");
1075 
1076     mpdm_ref(filename);
1077     mpdm_ref(mode);
1078 
1079     if (filename != NULL && mode != NULL) {
1080         /* convert to mbs,s */
1081         fn = mpdm_ref(MPDM_2MBS(filename->data));
1082         fm = mpdm_ref(MPDM_2MBS(mode->data));
1083 
1084         if ((f = fopen((char *) fn->data, (char *) fm->data)) == NULL)
1085             store_syserr();
1086         else {
1087 #if defined(CONFOPT_SYS_STAT_H) && defined(S_ISDIR) && defined(EISDIR)
1088             struct stat s;
1089 
1090             /* test if the open file is a directory */
1091             if (fstat(fileno(f), &s) != -1 && S_ISDIR(s.st_mode)) {
1092                 /* it's a directory; fail */
1093                 errno = EISDIR;
1094                 store_syserr();
1095                 fclose(f);
1096                 f = NULL;
1097             }
1098 #endif
1099         }
1100 
1101         mpdm_unref(fm);
1102         mpdm_unref(fn);
1103     }
1104 
1105     mpdm_unref(mode);
1106     mpdm_unref(filename);
1107 
1108     return f ? MPDM_F(f) : NULL;
1109 }
1110 
1111 
1112 /**
1113  * mpdm_read - Reads a line from a file descriptor.
1114  * @fd: the value containing the file descriptor
1115  *
1116  * Reads a line from @fd. Returns the line, or NULL on EOF.
1117  * [File Management]
1118  * [Character Set Conversion]
1119  */
mpdm_read(const mpdm_t fd)1120 mpdm_t mpdm_read(const mpdm_t fd)
1121 {
1122     mpdm_t v = NULL;
1123 
1124     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1125         wchar_t *ptr;
1126         int s = 0;
1127         int eol = -1;
1128         struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1129 
1130         ptr = fs->f_read(fs, &s, &eol);
1131 
1132         if (ptr != NULL) {
1133             /* something read; does it have an eol? */
1134             if (eol != -1) {
1135                 /* store */
1136                 wcsncpy(fs->eol, &ptr[eol], MAX_EOL);
1137 
1138                 /* if auto_chomp is set, delete the eol */
1139                 if (fs->auto_chomp) {
1140                     s -= wcslen(fs->eol);
1141                     ptr[s] = L'\0';
1142                 }
1143             }
1144             else
1145                 fs->eol[0] = L'\0';
1146 
1147             /* return the line */
1148             v = MPDM_ENS(ptr, s);
1149         }
1150         else {
1151             /* nothing read; if last read had an eol,
1152                return an empty string as the last read */
1153             if (fs->eol[0]) {
1154                 v = MPDM_S(L"");
1155                 fs->eol[0] = L'\0';
1156             }
1157         }
1158     }
1159 
1160     return v;
1161 }
1162 
1163 
mpdm_eol(mpdm_t fd)1164 wchar_t *mpdm_eol(mpdm_t fd)
1165 {
1166     wchar_t *r = NULL;
1167 
1168     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1169         struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1170 
1171         r = fs->eol;
1172     }
1173 
1174     return r;
1175 }
1176 
1177 
mpdm_getchar(const mpdm_t fd)1178 mpdm_t mpdm_getchar(const mpdm_t fd)
1179 {
1180     mpdm_t r = NULL;
1181 
1182     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1183         int c;
1184         wchar_t tmp[2];
1185         struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1186 
1187         if ((c = get_byte(fs)) != EOF) {
1188             /* get the char as-is */
1189             tmp[0] = (wchar_t) c;
1190             tmp[1] = L'\0';
1191 
1192             r = MPDM_S(tmp);
1193         }
1194     }
1195 
1196     return r;
1197 }
1198 
1199 
mpdm_putchar(const mpdm_t fd,const mpdm_t c)1200 int mpdm_putchar(const mpdm_t fd, const mpdm_t c)
1201 {
1202     int r = 1;
1203 
1204     mpdm_ref(c);
1205 
1206     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1207         struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1208         const wchar_t *ptr = mpdm_string(c);
1209 
1210         if (put_char(*ptr, fs) == -1)
1211             r = 0;
1212     }
1213 
1214     mpdm_unref(c);
1215 
1216     return r;
1217 }
1218 
1219 
1220 /**
1221  * mpdm_write - Writes a value into a file.
1222  * @fd: the file descriptor.
1223  * @v: the value to be written.
1224  *
1225  * Writes the @v value into @fd, using the current encoding. If @v is
1226  * an array or file, it's iterated and its elements written into @fd.
1227  * [File Management]
1228  * [Character Set Conversion]
1229  */
mpdm_write(const mpdm_t fd,const mpdm_t v)1230 int mpdm_write(const mpdm_t fd, const mpdm_t v)
1231 {
1232     int ret = -1;
1233 
1234     mpdm_ref(v);
1235 
1236     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1237         if (mpdm_type(v) == MPDM_TYPE_ARRAY || mpdm_type(v) == MPDM_TYPE_FILE) {
1238             int n = 0;
1239             mpdm_t w;
1240 
1241             while (mpdm_iterator(v, &n, &w, NULL))
1242                 ret = mpdm_write(fd, w);
1243         }
1244         else {
1245             struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1246             ret = fs->f_write(fs, mpdm_string(v));
1247         }
1248     }
1249 
1250     mpdm_unref(v);
1251 
1252     return ret;
1253 }
1254 
1255 
mpdm_fseek(const mpdm_t fd,long offset,int whence)1256 int mpdm_fseek(const mpdm_t fd, long offset, int whence)
1257 {
1258     struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1259 
1260     return fseek(fs->in, offset, whence);
1261 }
1262 
1263 
mpdm_ftell(const mpdm_t fd)1264 long mpdm_ftell(const mpdm_t fd)
1265 {
1266     struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1267 
1268     return ftell(fs->in);
1269 }
1270 
1271 
mpdm_feof(const mpdm_t fd)1272 int mpdm_feof(const mpdm_t fd)
1273 {
1274     struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1275 
1276     return feof(fs->in);
1277 }
1278 
1279 
mpdm_flock(mpdm_t fd,int operation)1280 int mpdm_flock(mpdm_t fd, int operation)
1281 {
1282     int ret = 0;
1283 
1284 #ifndef WIN32
1285 
1286     int i = -1;
1287     FILE *f = mpdm_get_filehandle(fd);
1288 
1289     if (f != NULL)
1290         i = fileno(f);
1291 
1292     ret = flock(i, operation);
1293 
1294 #endif /* WIN32 */
1295 
1296     return ret;
1297 }
1298 
1299 
mpdm_get_filehandle(const mpdm_t fd)1300 FILE *mpdm_get_filehandle(const mpdm_t fd)
1301 {
1302     FILE *f = NULL;
1303 
1304     if (mpdm_type(fd) == MPDM_TYPE_FILE) {
1305         struct mpdm_file *fs = (struct mpdm_file *) fd->data;
1306         f = fs->in;
1307     }
1308 
1309     return f;
1310 }
1311 
1312 
1313 /*
1314 mpdm_t mpdm_bread(mpdm_t fd, int size)
1315 {
1316 }
1317 
1318 
1319 int mpdm_bwrite(mpdm_tfd, mpdm_t v, int size)
1320 {
1321 }
1322 */
1323 
1324 
embedded_encodings(void)1325 static mpdm_t embedded_encodings(void)
1326 {
1327     mpdm_t e;
1328     wchar_t *e2e[] = {
1329         L"utf-8",      L"utf-8",
1330         L"utf8",       NULL,
1331         L"iso8859-1",  L"iso8859-1",
1332         L"iso-8859-1", NULL,
1333         L"8bit",       NULL,
1334         L"latin1",     NULL,
1335         L"latin-1",    NULL,
1336         L"utf-16le",   L"utf-16le",
1337         L"utf16le",    NULL,
1338         L"ucs-2le",    NULL,
1339         L"utf-16be",   L"utf-16be",
1340         L"utf16be",    NULL,
1341         L"ucs-2be",    NULL,
1342         L"utf-16",     L"utf-16",
1343         L"utf16",      NULL,
1344         L"ucs-2",      NULL,
1345         L"ucs2",       NULL,
1346         L"utf-32le",   L"utf-32le",
1347         L"utf32le",    NULL,
1348         L"ucs-4le",    NULL,
1349         L"utf-32be",   L"utf-32be",
1350         L"utf32be",    NULL,
1351         L"ucs-4be",    NULL,
1352         L"utf-32",     L"utf-32",
1353         L"utf32",      NULL,
1354         L"ucs-4",      NULL,
1355         L"ucs4",       NULL,
1356         L"utf-8bom",   L"utf-8bom",
1357         L"utf8bom",    NULL,
1358         L"msdos-437",  L"msdos-437",
1359         L"437",        NULL,
1360         L"cp-437",     NULL,
1361         L"cp437",      NULL,
1362         L"msdos-850",  L"msdos-850",
1363         L"850",        NULL,
1364         L"cp-850",     NULL,
1365         L"cp850",      NULL,
1366         L"windows-1252", L"windows-1252",
1367         NULL,          NULL
1368     };
1369 
1370     if ((e = mpdm_get_wcs(mpdm_root(), L"EMBEDDED_ENCODINGS")) == NULL) {
1371         int n;
1372         mpdm_t p = NULL;
1373 
1374         e = mpdm_set_wcs(mpdm_root(), MPDM_O(), L"EMBEDDED_ENCODINGS");
1375 
1376         for (n = 0; e2e[n] != NULL; n += 2) {
1377             mpdm_t v = MPDM_S(e2e[n]);
1378 
1379             if (e2e[n + 1] != NULL)
1380                 p = MPDM_S(e2e[n + 1]);
1381 
1382             mpdm_set(e, p, v);
1383             mpdm_set(e, p, mpdm_ulc(v, 1));
1384         }
1385 
1386     }
1387 
1388     return e;
1389 }
1390 
1391 
1392 /**
1393  * mpdm_encoding - Sets the current charset encoding for files.
1394  * @charset: the charset name.
1395  *
1396  * Sets the current charset encoding for files. Future opened
1397  * files will be assumed to be encoded with @charset, which can
1398  * be any of the supported charset names (utf-8, iso-8859-1, etc.),
1399  * and converted on each read / write. If charset is NULL, it
1400  * is reverted to default charset conversion (i.e. the one defined
1401  * in the locale).
1402  *
1403  * This function stores the @charset value into the ENCODING item
1404  * of the mpdm_root() hash.
1405  *
1406  * Returns a negative number if @charset is unsupported, or zero
1407  * if no errors were found.
1408  * [File Management]
1409  * [Character Set Conversion]
1410  */
mpdm_encoding(mpdm_t charset)1411 int mpdm_encoding(mpdm_t charset)
1412 {
1413     int ret = -1;
1414     mpdm_t e = embedded_encodings();
1415     mpdm_t v = NULL;
1416 
1417     mpdm_ref(charset);
1418 
1419     /* NULL encoding? done */
1420     if (mpdm_size(charset) == 0) {
1421         mpdm_set_wcs(mpdm_root(), NULL, L"ENCODING");
1422         ret = 0;
1423     }
1424 
1425 #ifdef CONFOPT_ICONV
1426     else {
1427         iconv_t ic;
1428         mpdm_t cs = mpdm_ref(MPDM_2MBS(charset->data));
1429 
1430         /* tries to create an iconv encoder and decoder for this charset */
1431 
1432         if ((ic = iconv_open("WCHAR_T", (char *) cs->data)) == (iconv_t) - 1)
1433             ret = -1;
1434         else {
1435             iconv_close(ic);
1436 
1437             if ((ic = iconv_open((char *) cs->data, "WCHAR_T")) == (iconv_t) - 1)
1438                 ret = -2;
1439             else {
1440                 iconv_close(ic);
1441 
1442                 /* got a valid encoding */
1443                 v = charset;
1444                 ret = 0;
1445             }
1446         }
1447 
1448         mpdm_unref(cs);
1449     }
1450 #endif                          /* CONFOPT_ICONV */
1451 
1452     if (ret != 0 && (v = mpdm_get(e, charset)) != NULL)
1453         ret = 0;
1454 
1455     if (ret == 0)
1456         mpdm_set_wcs(mpdm_root(), v, L"ENCODING");
1457 
1458     mpdm_unref(charset);
1459 
1460     return ret;
1461 }
1462 
1463 
1464 /**
1465  * mpdm_unlink - Deletes a file.
1466  * @filename: file name to be deleted
1467  *
1468  * Deletes a file.
1469  * [File Management]
1470  */
mpdm_unlink(const mpdm_t filename)1471 int mpdm_unlink(const mpdm_t filename)
1472 {
1473     int ret;
1474     mpdm_t fn;
1475 
1476     mpdm_ref(filename);
1477 
1478     /* convert to mbs */
1479     fn = mpdm_ref(MPDM_2MBS(filename->data));
1480 
1481     if ((ret = unlink((char *) fn->data)) == -1)
1482         store_syserr();
1483 
1484     mpdm_unref(fn);
1485     mpdm_unref(filename);
1486 
1487     return ret;
1488 }
1489 
1490 
1491 /**
1492  * mpdm_rename - Renames a file.
1493  * @o: old path
1494  * @n: new path
1495  *
1496  * Renames a file.
1497  * [File Management]
1498  */
mpdm_rename(const mpdm_t o,const mpdm_t n)1499 int mpdm_rename(const mpdm_t o, const mpdm_t n)
1500 {
1501     int ret;
1502     mpdm_t om, nm;
1503 
1504     mpdm_ref(o);
1505     mpdm_ref(n);
1506 
1507     om = mpdm_ref(MPDM_2MBS(o->data));
1508     nm = mpdm_ref(MPDM_2MBS(n->data));
1509 
1510     if ((ret = rename((char *)om->data, (char *)nm->data)) == -1)
1511         store_syserr();
1512 
1513     mpdm_unref(nm);
1514     mpdm_unref(om);
1515     mpdm_unref(n);
1516     mpdm_unref(o);
1517 
1518     return ret;
1519 }
1520 
1521 
1522 /**
1523  * mpdm_stat - Gives status from a file.
1524  * @filename: file name to get the status from
1525  *
1526  * Returns a 14 element array of the status (permissions, onwer, etc.)
1527  * from the desired @filename, or NULL if the file cannot be accessed.
1528  * (man 2 stat).
1529  *
1530  * The values are: 0, device number of filesystem; 1, inode number;
1531  * 2, file mode; 3, number of hard links to the file; 4, uid; 5, gid;
1532  * 6, device identifier; 7, total size of file in bytes; 8, atime;
1533  * 9, mtime; 10, ctime; 11, preferred block size for system I/O;
1534  * 12, number of blocks allocated and 13, canonicalized file name.
1535  * Not all elements have necesarily meaningful values, as most are
1536  * system-dependent.
1537  * [File Management]
1538  */
mpdm_stat(const mpdm_t filename)1539 mpdm_t mpdm_stat(const mpdm_t filename)
1540 {
1541     mpdm_t r = NULL;
1542 
1543     mpdm_ref(filename);
1544 
1545 #ifdef CONFOPT_SYS_STAT_H
1546     struct stat s;
1547     mpdm_t fn;
1548 
1549     fn = mpdm_ref(MPDM_2MBS(filename->data));
1550 
1551     if (stat((char *) fn->data, &s) != -1) {
1552         r = MPDM_A(14);
1553 
1554         mpdm_ref(r);
1555 
1556         mpdm_set_i(r, MPDM_I(s.st_dev), 0);
1557         mpdm_set_i(r, MPDM_I(s.st_ino), 1);
1558         mpdm_set_i(r, MPDM_I(s.st_mode), 2);
1559         mpdm_set_i(r, MPDM_I(s.st_nlink), 3);
1560         mpdm_set_i(r, MPDM_I(s.st_uid), 4);
1561         mpdm_set_i(r, MPDM_I(s.st_gid), 5);
1562         mpdm_set_i(r, MPDM_I(s.st_rdev), 6);
1563         mpdm_set_i(r, MPDM_I(s.st_size), 7);
1564         mpdm_set_i(r, MPDM_I(s.st_atime), 8);
1565         mpdm_set_i(r, MPDM_I(s.st_mtime), 9);
1566         mpdm_set_i(r, MPDM_I(s.st_ctime), 10);
1567         mpdm_set_i(r, MPDM_I(0), 11);    /* s.st_blksize */
1568         mpdm_set_i(r, MPDM_I(0), 12);    /* s.st_blocks */
1569 
1570 #ifdef CONFOPT_CANONICALIZE_FILE_NAME
1571 
1572         {
1573             char *ptr;
1574 
1575             if ((ptr = canonicalize_file_name((char *) fn->data)) != NULL) {
1576                 mpdm_set_i(r, MPDM_MBS(ptr), 13);
1577                 free(ptr);
1578             }
1579         }
1580 #endif
1581 
1582 #ifdef CONFOPT_REALPATH
1583         {
1584             char tmp[2048];
1585 
1586             if (realpath((char *) fn->data, tmp) != NULL)
1587                 mpdm_set_i(r, MPDM_MBS(tmp), 13);
1588         }
1589 #endif
1590 
1591 #ifdef CONFOPT_FULLPATH
1592         {
1593             char tmp[_MAX_PATH + 1];
1594 
1595             if (_fullpath(tmp, (char *) fn->data, _MAX_PATH) != NULL)
1596                 mpdm_set_i(r, MPDM_MBS(tmp), 13);
1597         }
1598 #endif
1599 
1600         mpdm_unrefnd(r);
1601     }
1602     else
1603         store_syserr();
1604 
1605     mpdm_unref(fn);
1606 
1607 #endif                          /* CONFOPT_SYS_STAT_H */
1608 
1609     mpdm_unref(filename);
1610 
1611     return r;
1612 }
1613 
1614 
1615 /**
1616  * mpdm_chmod - Changes a file's permissions.
1617  * @filename: the file name
1618  * @perms: permissions (element 2 from mpdm_stat())
1619  *
1620  * Changes the permissions for a file.
1621  * [File Management]
1622  */
mpdm_chmod(const mpdm_t filename,mpdm_t perms)1623 int mpdm_chmod(const mpdm_t filename, mpdm_t perms)
1624 {
1625     int r = -1;
1626 
1627     mpdm_ref(filename);
1628     mpdm_ref(perms);
1629 
1630     mpdm_t fn = mpdm_ref(MPDM_2MBS(filename->data));
1631 
1632     if ((r = chmod((char *) fn->data, mpdm_ival(perms))) == -1)
1633         store_syserr();
1634 
1635     mpdm_unref(fn);
1636     mpdm_unref(perms);
1637     mpdm_unref(filename);
1638 
1639     return r;
1640 }
1641 
1642 
1643 /**
1644  * mpdm_chdir - Changes the working directory
1645  * @dir: the new path
1646  *
1647  * Changes the working directory
1648  * [File Management]
1649  */
mpdm_chdir(const mpdm_t dir)1650 int mpdm_chdir(const mpdm_t dir)
1651 {
1652     int r = -1;
1653 
1654     mpdm_ref(dir);
1655     mpdm_t fn = mpdm_ref(MPDM_2MBS(dir->data));
1656 
1657     if ((r = chdir((char *) fn->data)) == -1)
1658         store_syserr();
1659 
1660     mpdm_unref(fn);
1661     mpdm_unref(dir);
1662 
1663     return r;
1664 }
1665 
1666 
1667 /**
1668  * mpdm_getcwd - Get current working directory
1669  *
1670  * Returns the current working directory.
1671  * [File Management]
1672  */
mpdm_getcwd(void)1673 mpdm_t mpdm_getcwd(void)
1674 {
1675     char tmp[4096];
1676 
1677     getcwd(tmp, sizeof(tmp) - 1);
1678     tmp[sizeof(tmp) - 1] = '\0';
1679 
1680     return MPDM_MBS(tmp);
1681 }
1682 
1683 
1684 /**
1685  * mpdm_chown - Changes a file's owner.
1686  * @filename: the file name
1687  * @uid: user id (element 4 from mpdm_stat())
1688  * @gid: group id (element 5 from mpdm_stat())
1689  *
1690  * Changes the owner and group id's for a file.
1691  * [File Management]
1692  */
mpdm_chown(const mpdm_t filename,mpdm_t uid,mpdm_t gid)1693 int mpdm_chown(const mpdm_t filename, mpdm_t uid, mpdm_t gid)
1694 {
1695     int r = -1;
1696 
1697     mpdm_ref(filename);
1698     mpdm_ref(uid);
1699     mpdm_ref(gid);
1700 
1701 #ifdef CONFOPT_CHOWN
1702 
1703     mpdm_t fn = mpdm_ref(MPDM_2MBS(filename->data));
1704 
1705     if ((r = chown((char *) fn->data, mpdm_ival(uid), mpdm_ival(gid))) == -1)
1706         store_syserr();
1707 
1708     mpdm_unref(fn);
1709 
1710 #endif /* CONFOPT_CHOWN */
1711 
1712     mpdm_unref(gid);
1713     mpdm_unref(uid);
1714     mpdm_unref(filename);
1715 
1716     return r;
1717 }
1718 
1719 
1720 /**
1721  * mpdm_glob - Executes a file globbing.
1722  * @spec: Globbing spec
1723  * @base: Optional base directory
1724  *
1725  * Executes a file globbing. @spec is system-dependent, but usually
1726  * the * and ? metacharacters work everywhere. @base can contain a
1727  * directory; if that's the case, the output strings will include it.
1728  * In any case, each returned value will be suitable for a call to
1729  * mpdm_open().
1730  *
1731  * Returns an array of files that match the globbing (can be an empty
1732  * array if no file matches), or NULL if globbing is unsupported.
1733  * [File Management]
1734  */
mpdm_glob(mpdm_t spec,mpdm_t base)1735 mpdm_t mpdm_glob(mpdm_t spec, mpdm_t base)
1736 {
1737     mpdm_t d, f;
1738     char *ptr;
1739 
1740 #ifdef CONFOPT_WIN32
1741     WIN32_FIND_DATA fd;
1742     HANDLE h;
1743     const wchar_t *def_spec = L"*.*";
1744 #endif
1745 
1746 #if CONFOPT_GLOB_H
1747     glob_t globbuf;
1748     const wchar_t *def_spec = L"*";
1749 #endif
1750 
1751     /* build full path */
1752     if (base != NULL) {
1753         base = mpdm_strcat_wcs(base, L"/");
1754 
1755         /* escape expandable chars */
1756         base = mpdm_sregex(base, MPDM_S(L"@[]\\[]@g"), MPDM_S(L"\\\\&"), 0);
1757     }
1758 
1759     if (spec == NULL)
1760         spec = mpdm_strcat_wcs(base, def_spec);
1761     else
1762         spec = mpdm_strcat(base, spec);
1763 
1764     /* delete repeated directory delimiters */
1765     spec = mpdm_sregex(spec, MPDM_S(L"@[\\/]{2,}@g"), MPDM_S(L"/"), 0);
1766 
1767     mpdm_ref(spec);
1768     ptr = mpdm_wcstombs(mpdm_string(spec), NULL);
1769     mpdm_unref(spec);
1770 
1771     d = MPDM_A(0);
1772     f = MPDM_A(0);
1773 
1774 #ifdef CONFOPT_WIN32
1775     if ((h = FindFirstFile(ptr, &fd)) != INVALID_HANDLE_VALUE) {
1776         mpdm_t p;
1777         char *b;
1778 
1779         /* if spec includes a directory, store in s */
1780         if ((b = strrchr(ptr, '/')) != NULL) {
1781             *(b + 1) = '\0';
1782             p = MPDM_MBS(ptr);
1783         }
1784         else
1785             p = NULL;
1786 
1787         mpdm_ref(p);
1788 
1789         do {
1790             mpdm_t t;
1791 
1792             /* ignore . and .. */
1793             if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0)
1794                 continue;
1795 
1796             /* concat base directory and file names */
1797             t = mpdm_strcat(p, MPDM_MBS(fd.cFileName));
1798 
1799             /* if it's a directory, add a / */
1800             if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1801                 mpdm_push(d, mpdm_strcat_wcs(t, L"/"));
1802             else
1803                 mpdm_push(f, t);
1804         }
1805         while (FindNextFile(h, &fd));
1806 
1807         FindClose(h);
1808 
1809         mpdm_unref(p);
1810     }
1811 #endif
1812 
1813 #if CONFOPT_GLOB_H
1814     globbuf.gl_offs = 1;
1815 
1816     if (glob(ptr, GLOB_MARK, NULL, &globbuf) == 0) {
1817         int n;
1818 
1819         for (n = 0; globbuf.gl_pathv[n] != NULL; n++) {
1820             char *p = globbuf.gl_pathv[n];
1821             mpdm_t t = MPDM_MBS(p);
1822 
1823             /* if last char is /, add to directories */
1824             if (p[strlen(p) - 1] == '/')
1825                 mpdm_push(d, t);
1826             else
1827                 mpdm_push(f, t);
1828         }
1829     }
1830 
1831     globfree(&globbuf);
1832 #endif
1833 
1834     free(ptr);
1835 
1836     mpdm_sort(d, 1);
1837     mpdm_sort(f, 1);
1838 
1839     return mpdm_join(d, f);
1840 }
1841 
1842 
1843 /** pipes **/
1844 
1845 
1846 #ifdef CONFOPT_WIN32
1847 
win32_pipe(HANDLE * h,int n)1848 static void win32_pipe(HANDLE *h, int n)
1849 {
1850     SECURITY_ATTRIBUTES sa;
1851     HANDLE cp, t;
1852 
1853     memset(&sa, '\0', sizeof(sa));
1854     sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
1855     sa.bInheritHandle       = TRUE;
1856     sa.lpSecurityDescriptor = NULL;
1857 
1858     cp = GetCurrentProcess();
1859 
1860     CreatePipe(&h[0], &h[1], &sa, 0);
1861     DuplicateHandle(cp, h[n], cp, &t, 0, FALSE, DUPLICATE_SAME_ACCESS);
1862     CloseHandle(h[n]);
1863     h[n] = t;
1864 }
1865 
1866 
sysdep_popen(mpdm_t v,char * prg,int rw)1867 static int sysdep_popen(mpdm_t v, char *prg, int rw)
1868 /* win32-style pipe */
1869 {
1870     HANDLE pr[2];
1871     HANDLE pw[2];
1872     PROCESS_INFORMATION pi;
1873     STARTUPINFO si;
1874     int ret;
1875     struct mpdm_file *fs = (struct mpdm_file *) v->data;
1876 
1877     fs->is_pipe = 1;
1878 
1879     /* init all */
1880     pr[0] = pr[1] = pw[0] = pw[1] = NULL;
1881 
1882     if (rw & 0x01)
1883         win32_pipe(pr, 0);
1884     if (rw & 0x02)
1885         win32_pipe(pw, 1);
1886 
1887     /* spawn new process */
1888     memset(&pi, '\0', sizeof(pi));
1889     memset(&si, '\0', sizeof(si));
1890 
1891     si.cb = sizeof(STARTUPINFO);
1892     si.hStdError = pr[1];
1893     si.hStdOutput = pr[1];
1894     si.hStdInput = pw[0];
1895     si.dwFlags |= STARTF_USESTDHANDLES;
1896 
1897     ret = CreateProcess(NULL, prg, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
1898 
1899     if (rw & 0x01)
1900         CloseHandle(pr[1]);
1901     if (rw & 0x02)
1902         CloseHandle(pw[0]);
1903 
1904     fs->hin     = pr[0];
1905     fs->hout    = pw[1];
1906     fs->process = pi.hProcess;
1907 
1908     return ret;
1909 }
1910 
1911 
sysdep_pclose(const mpdm_t v)1912 static int sysdep_pclose(const mpdm_t v)
1913 {
1914     struct mpdm_file *fs = (struct mpdm_file *) v->data;
1915     DWORD out = 0;
1916 
1917     if (fs->hin != NULL)
1918         CloseHandle(fs->hin);
1919 
1920     if (fs->hout != fs->hin && fs->hout != NULL)
1921         CloseHandle(fs->hout);
1922 
1923     fs->hin = NULL;
1924     fs->hout = NULL;
1925 
1926     if (!fs->skip_wait) {
1927         /* waits until the process terminates */
1928         WaitForSingleObject(fs->process, 1000);
1929         GetExitCodeProcess(fs->process, &out);
1930     } else fs->skip_wait = 0;
1931     return (int) out;
1932 }
1933 
1934 
1935 #else /* CONFOPT_WIN32 */
1936 
1937 extern char **environ;
1938 
sysdep_popen(mpdm_t v,char * prg,int rw)1939 static int sysdep_popen(mpdm_t v, char *prg, int rw)
1940 /* unix-style pipe open */
1941 {
1942     int pr[2], pw[2];
1943     struct mpdm_file *fs = (struct mpdm_file *) v->data;
1944 
1945     fs->is_pipe = 1;
1946 
1947     /* init all */
1948     pr[0] = pr[1] = pw[0] = pw[1] = -1;
1949 
1950     if (rw & 0x01)
1951         pipe(pr);
1952     if (rw & 0x02)
1953         pipe(pw);
1954 
1955     if (fork() == 0) {
1956         /* child process */
1957         mpdm_t v;
1958         int n;
1959 
1960         setsid();
1961 
1962         if (rw & 0x01) {
1963             close(1);
1964             dup(pr[1]);
1965             close(pr[0]);
1966         }
1967         if (rw & 0x02) {
1968             close(0);
1969             dup(pw[0]);
1970             close(pw[1]);
1971         }
1972 
1973         /* redirect stderr to stdout */
1974         close(2);
1975         dup(1);
1976 
1977         /* build the environment for the subprocess */
1978         v = mpdm_join(mpdm_get_wcs(mpdm_root(), L"ENV"), MPDM_S(L"="));
1979         environ = (char **) calloc(sizeof(char *), mpdm_size(v) + 1);
1980 
1981         mpdm_ref(v);
1982         for (n = 0; n < mpdm_size(v); n++) {
1983             mpdm_t w = MPDM_2MBS(mpdm_string(mpdm_get_i(v, n)));
1984             environ[n] = (char *)w->data;
1985         }
1986         mpdm_unref(v);
1987 
1988         /* run the program */
1989         execlp("/bin/sh", "/bin/sh", "-c", prg, NULL);
1990         execlp(prg, prg, NULL);
1991 
1992         /* still here? exec failed; close pipes and exit */
1993         close(0);
1994         close(1);
1995         exit(0);
1996     }
1997 
1998     /* create the pipes as non-buffered streams */
1999     if (rw & 0x01) {
2000         fs->in = fdopen(pr[0], "r");
2001         setvbuf(fs->in, NULL, _IONBF, 0);
2002         close(pr[1]);
2003     }
2004 
2005     if (rw & 0x02) {
2006         fs->out = fdopen(pw[1], "w");
2007         setvbuf(fs->out, NULL, _IONBF, 0);
2008         close(pw[0]);
2009     }
2010 
2011     return 1;
2012 }
2013 
2014 
sysdep_pclose(const mpdm_t v)2015 static int sysdep_pclose(const mpdm_t v)
2016 /* unix-style pipe close */
2017 {
2018     int s = 0;
2019     struct mpdm_file *fs = (struct mpdm_file *) v->data;
2020 
2021     if (!fs->skip_wait)
2022         wait(&s);
2023     else fs->skip_wait = 0;
2024 
2025     return s;
2026 }
2027 
2028 
2029 #endif /* CONFOPT_WIN32 */
2030 
2031 
2032 /**
2033  * mpdm_popen - Opens a pipe.
2034  * @prg: the program to pipe
2035  * @mode: an fopen-like mode string
2036  *
2037  * Opens a pipe to a program. If @prg can be open in the specified @mode, an
2038  * mpdm_t value will be returned containing the file descriptor, or NULL
2039  * otherwise.
2040  * [File Management]
2041  */
mpdm_popen(const mpdm_t prg,const mpdm_t mode)2042 mpdm_t mpdm_popen(const mpdm_t prg, const mpdm_t mode)
2043 {
2044     mpdm_t v = NULL;
2045 
2046     mpdm_ref(prg);
2047     mpdm_ref(mode);
2048 
2049     if (prg != NULL && mode != NULL) {
2050         mpdm_t pr, md;
2051         char *m;
2052         int rw = 0;
2053 
2054         v = MPDM_F(NULL);
2055 
2056         /* convert to mbs,s */
2057         pr = mpdm_ref(MPDM_2MBS(prg->data));
2058         md = mpdm_ref(MPDM_2MBS(mode->data));
2059 
2060         /* get the mode */
2061         m = (char *) md->data;
2062 
2063         /* set the mode */
2064         if (m[0] == 'r')
2065             rw = 0x01;
2066         if (m[0] == 'w')
2067             rw = 0x02;
2068         if (m[1] == '+')
2069             rw = 0x03;          /* r+ or w+ */
2070 
2071         if (!sysdep_popen(v, (char *) pr->data, rw)) {
2072             mpdm_void(v);
2073             v = NULL;
2074         } else {
2075             /* need to wait on first pclose call */
2076             struct mpdm_file *fs = (struct mpdm_file *) v->data;
2077             fs->skip_wait = 0;
2078         }
2079 
2080         mpdm_unref(md);
2081         mpdm_unref(pr);
2082     }
2083 
2084     mpdm_unref(mode);
2085     mpdm_unref(prg);
2086 
2087     return v;
2088 }
2089 
2090 
2091 /**
2092  * mpdm_popen2 - Opens a pipe and returns 2 descriptors.
2093  * @prg: the program to pipe
2094  *
2095  * Opens a read-write pipe and returns an array of two descriptors,
2096  * one for reading and one for writing. If @prg could not be piped to,
2097  * returns NULL.
2098  * [File Management]
2099  */
mpdm_popen2(const mpdm_t prg)2100 mpdm_t mpdm_popen2(const mpdm_t prg)
2101 {
2102     mpdm_t i, o;
2103     mpdm_t p = NULL;
2104 
2105     if ((i = mpdm_popen(prg, MPDM_S(L"r+"))) != NULL) {
2106         struct mpdm_file *ifs;
2107         struct mpdm_file *ofs;
2108 
2109         o = MPDM_C(i->type, (void *)i->data, i->size);
2110 
2111         ifs = (struct mpdm_file *)i->data;
2112         ofs = (struct mpdm_file *)o->data;
2113 
2114         ofs->in = ifs->out;
2115         ifs->out = NULL;
2116 	/* make sure that when closing write pipe, it does not wait for the child's end */
2117 	ofs->skip_wait = 1;
2118 
2119 #ifdef CONFOPT_WIN32
2120         ofs->hin = ifs->hout;
2121         ifs->hout = NULL;
2122 #endif
2123 
2124         p = mpdm_ref(MPDM_A(2));
2125         mpdm_set_i(p, i, 0);
2126         mpdm_set_i(p, o, 1);
2127         mpdm_unrefnd(p);
2128     }
2129 
2130     return p;
2131 }
2132 
2133 
2134 /**
2135  * mpdm_pclose - Closes a pipe.
2136  * @fd: the value containing the file descriptor
2137  *
2138  * Closes a pipe.
2139  * [File Management]
2140  */
mpdm_pclose(mpdm_t fd)2141 int mpdm_pclose(mpdm_t fd)
2142 {
2143     return mpdm_close(fd);
2144 }
2145 
2146 
2147 /**
2148  * mpdm_home_dir - Returns the home user directory.
2149  *
2150  * Returns a system-dependent directory where the user can write
2151  * documents and create subdirectories.
2152  * [File Management]
2153  */
mpdm_home_dir(void)2154 mpdm_t mpdm_home_dir(void)
2155 {
2156     mpdm_t r = NULL;
2157     char *ptr = "";
2158     wchar_t *wptr = L"";
2159     char tmp[512] = "";
2160 
2161     memset(tmp, '\0', sizeof(tmp));
2162 
2163 #ifdef CONFOPT_WIN32
2164 
2165     LPITEMIDLIST pidl;
2166 
2167     /* get the 'My Documents' folder */
2168     SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
2169     SHGetPathFromIDList(pidl, tmp);
2170     wptr = L"\\";
2171 
2172 #endif
2173 
2174 #ifdef CONFOPT_PWD_H
2175 
2176     struct passwd *p;
2177 
2178     /* get home dir from /etc/passwd entry */
2179     if (tmp[0] == '\0' && (p = getpwuid(getpid())) != NULL) {
2180         strncpy(tmp, p->pw_dir, sizeof(tmp) - 1);
2181         wptr = L"/";
2182     }
2183 
2184 #endif
2185 
2186     /* still none? try the ENV variable $HOME */
2187     if (tmp[0] == '\0' && (ptr = getenv("HOME")) != NULL) {
2188         strncpy(tmp, ptr, sizeof(tmp) - 1);
2189         wptr = L"/";
2190     }
2191 
2192     if (tmp[0] != '\0')
2193         r = mpdm_strcat_wcs(MPDM_MBS(tmp), wptr);
2194 
2195     return r;
2196 }
2197 
2198 
2199 /**
2200  * mpdm_app_dir - Returns the applications directory.
2201  *
2202  * Returns a system-dependent directory where the applications store
2203  * their private data, as components or resources.
2204  *
2205  * If the global APPID MPDM variable is set, it's used to search for
2206  * the specific application installation folder (on MS Windows' registry)
2207  * and / or appended as the final folder.
2208  * [File Management]
2209  */
mpdm_app_dir(void)2210 mpdm_t mpdm_app_dir(void)
2211 {
2212     mpdm_t r = NULL;
2213     mpdm_t appid = mpdm_get_wcs(mpdm_root(), L"APPID");
2214     int aok = 0;
2215 
2216 #ifdef CONFOPT_WIN32
2217 
2218     HKEY hkey;
2219     char tmp[MAX_PATH];
2220     LPITEMIDLIST pidl;
2221 
2222     tmp[0] = '\0';
2223 
2224     if (appid != NULL) {
2225         /* find the installation folder in the registry */
2226         char *ptr = mpdm_wcstombs(mpdm_string(appid), NULL);
2227 
2228         sprintf(tmp, "SOFTWARE\\%s\\appdir", ptr);
2229 
2230         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, tmp, 0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
2231             int n = sizeof(tmp);
2232 
2233             if (RegQueryValueEx(hkey, NULL, NULL, NULL,
2234                 (LPBYTE) tmp, (LPDWORD) &n) == ERROR_SUCCESS)
2235                     aok = 1;
2236                 else
2237                     tmp[0] = '\0';
2238         }
2239         else
2240             tmp[0] = '\0';
2241 
2242         free(ptr);
2243     }
2244 
2245     if (tmp[0] == '\0') {
2246         /* get the 'Program Files' folder (can fail) */
2247         if (SHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidl) == S_OK)
2248             SHGetPathFromIDList(pidl, tmp);
2249 
2250         /* if it's still empty, get from the registry */
2251         if (tmp[0] == '\0' && RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2252                      "SOFTWARE\\Microsoft\\Windows\\CurrentVersion",
2253                      0, KEY_QUERY_VALUE, &hkey) == ERROR_SUCCESS) {
2254             int n = sizeof(tmp);
2255 
2256             if (RegQueryValueEx(hkey, "ProgramFilesDir",
2257                                 NULL, NULL, (LPBYTE) tmp,
2258                                 (LPDWORD) & n) != ERROR_SUCCESS)
2259                 tmp[0] = '\0';
2260         }
2261     }
2262 
2263     if (tmp[0] != '\0') {
2264         r = mpdm_strcat_wcs(MPDM_MBS(tmp), L"\\");
2265     }
2266 #endif
2267 
2268     /* still none? get the configured directory */
2269     if (r == NULL)
2270         r = MPDM_MBS(CONFOPT_PREFIX "/share/");
2271 
2272     if (appid && !aok)
2273         r = mpdm_strcat(r, appid);
2274 
2275     return r;
2276 }
2277 
2278 
2279 /** sockets **/
2280 
init_sockets(void)2281 void init_sockets(void)
2282 {
2283     static int init = 0;
2284 
2285     if (init == 0) {
2286         init = 1;
2287 
2288 #ifdef CONFOPT_WIN32
2289         WSADATA wsaData;
2290         WSAStartup(MAKEWORD(2, 2), &wsaData);
2291 #endif
2292     }
2293 }
2294 
2295 
mpdm_connect(mpdm_t host,mpdm_t serv)2296 mpdm_t mpdm_connect(mpdm_t host, mpdm_t serv)
2297 /* opens a client socket to host:serv */
2298 {
2299     mpdm_t f = NULL;
2300     char *h;
2301     char *s;
2302     int d = -1;
2303 
2304     mpdm_ref(host);
2305     mpdm_ref(serv);
2306 
2307     h = mpdm_wcstombs(mpdm_string(host), NULL);
2308     s = mpdm_wcstombs(mpdm_string(serv), NULL);
2309 
2310     init_sockets();
2311 
2312     {
2313 
2314 #ifndef CONFOPT_WITHOUT_GETADDRINFO
2315 
2316     struct addrinfo *res;
2317     struct addrinfo hints;
2318 
2319     memset(&hints, '\0', sizeof(hints));
2320 
2321     hints.ai_socktype   = SOCK_STREAM;
2322     hints.ai_flags      = AI_ADDRCONFIG;
2323 
2324     if (getaddrinfo(h, s, &hints, &res) == 0) {
2325         struct addrinfo *r;
2326 
2327         for (r = res; r != NULL; r = r->ai_next) {
2328             d = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
2329 
2330             if (d != -1) {
2331                 if (connect(d, r->ai_addr, r->ai_addrlen) == 0)
2332                     break;
2333 
2334                 close(d);
2335                 d = -1;
2336             }
2337         }
2338 
2339         freeaddrinfo(res);
2340     }
2341 
2342 #else   /* CONFOPT_WITHOUT_GETADDRINFO */
2343 
2344     /* traditional socket interface */
2345     struct hostent *he;
2346 
2347     if ((he = gethostbyname(h)) != NULL) {
2348         struct servent *se;
2349 
2350         if ((se = getservbyname(s, "tcp")) != NULL) {
2351             struct sockaddr_in host;
2352 
2353             memset(&host, '\0', sizeof(host));
2354 
2355             memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
2356             host.sin_family = he->h_addrtype;
2357             host.sin_port   = se->s_port;
2358 
2359             if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
2360                 if (connect(d, (struct sockaddr *)&host, sizeof(host)) == -1)
2361                     d = -1;
2362             }
2363         }
2364     }
2365 
2366 #endif  /* CONFOPT_WITHOUT_GETADDRINFO */
2367 
2368     }
2369 
2370     /* create file value */
2371     if (d != -1) {
2372         struct mpdm_file *fs;
2373 
2374         f = MPDM_F(NULL);
2375         fs = (struct mpdm_file *) f->data;
2376 
2377         fs->sock = d;
2378     }
2379 
2380     free(s);
2381     free(h);
2382 
2383     mpdm_unref(serv);
2384     mpdm_unref(host);
2385 
2386     return f;
2387 }
2388 
2389 
mpdm_server(mpdm_t addr,mpdm_t port)2390 mpdm_t mpdm_server(mpdm_t addr, mpdm_t port)
2391 /* opens a server socket in addr:port, addr can be NULL */
2392 {
2393     mpdm_t f = NULL;
2394     char *h;
2395     int p;
2396     int d = -1;
2397     struct sockaddr_in host;
2398 
2399     mpdm_ref(addr);
2400     mpdm_ref(port);
2401 
2402     h = addr == NULL ? NULL : mpdm_wcstombs(mpdm_string(addr), NULL);
2403     p = mpdm_ival(port);
2404 
2405     init_sockets();
2406 
2407     memset(&host, '\0', sizeof(host));
2408 
2409     if (h != NULL) {
2410         struct hostent *he;
2411 
2412         if ((he = gethostbyname(h)) != NULL)
2413             memcpy(&host.sin_addr, he->h_addr_list[0], he->h_length);
2414         else
2415             goto end;
2416     }
2417 
2418     host.sin_family = AF_INET;
2419     host.sin_port   = htons(p);
2420 
2421     if ((d = socket(AF_INET, SOCK_STREAM, 0)) != -1) {
2422         /* reuse addr */
2423         int i = 1;
2424         setsockopt(d, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
2425 
2426         if (bind(d, (struct sockaddr *)&host, sizeof(host)) == -1) {
2427             close(d);
2428             d = -1;
2429         }
2430         else
2431             listen(d, SOMAXCONN);
2432     }
2433 
2434     /* create file value */
2435     if (d != -1) {
2436         struct mpdm_file *fs;
2437 
2438         f = MPDM_F(NULL);
2439         fs = (struct mpdm_file *) f->data;
2440 
2441         fs->sock = d;
2442     }
2443 
2444 end:
2445     free(h);
2446 
2447     mpdm_unref(port);
2448     mpdm_unref(addr);
2449 
2450     return f;
2451 }
2452 
2453 
mpdm_accept(mpdm_t sock)2454 mpdm_t mpdm_accept(mpdm_t sock)
2455 /* accepts a connection from a socket */
2456 {
2457     mpdm_t f = NULL;
2458     struct sockaddr_in host;
2459     struct mpdm_file *fs;
2460     unsigned int l = sizeof(host);
2461     int d = -1;
2462 
2463     fs = (struct mpdm_file *)sock->data;
2464 
2465     if ((d = accept(fs->sock, (struct sockaddr *)&host, &l)) != -1) {
2466         f = MPDM_F(NULL);
2467         fs = (struct mpdm_file *)f->data;
2468 
2469         fs->sock = d;
2470     }
2471 
2472     return f;
2473 }
2474 
file_close(mpdm_t v)2475 static int file_close(mpdm_t v)
2476 /* close any type of file / pipe / socket */
2477 {
2478     int r = -1;
2479     struct mpdm_file *fs;
2480 
2481     if (v && (fs = (struct mpdm_file *) v->data)) {
2482 #ifdef CONFOPT_ICONV
2483         if (fs->ic_enc != (iconv_t) - 1)
2484             iconv_close(fs->ic_enc);
2485 
2486         if (fs->ic_dec != fs->ic_enc && fs->ic_dec != (iconv_t) - 1)
2487             iconv_close(fs->ic_dec);
2488 
2489         fs->ic_enc = (iconv_t) - 1;
2490         fs->ic_dec = (iconv_t) - 1;
2491 #endif
2492 
2493         if (fs->in != NULL)
2494             r = fclose(fs->in);
2495 
2496         if (fs->out != fs->in && fs->out != NULL)
2497             r = fclose(fs->out);
2498 
2499         fs->in = NULL;
2500         fs->out = NULL;
2501 
2502         if (fs->is_pipe) {
2503             r = sysdep_pclose(v);
2504             fs->is_pipe = 0;
2505         }
2506 
2507         if (fs->sock != -1) {
2508 #ifdef CONFOPT_WIN32
2509             r = closesocket(fs->sock);
2510 #else
2511             r = close(fs->sock);
2512 #endif
2513             fs->sock = -1;
2514         }
2515     }
2516 
2517     return r;
2518 }
2519 
2520 
vc_file_destroy(mpdm_t v)2521 static mpdm_t vc_file_destroy(mpdm_t v)
2522 /* destroys an file value */
2523 {
2524     file_close(v);
2525 
2526     return v;
2527 }
2528 
2529 
mpdm_new_f(FILE * f)2530 mpdm_t mpdm_new_f(FILE *f)
2531 /* creates a new file value */
2532 {
2533     mpdm_t v = NULL;
2534     struct mpdm_file *fs;
2535     mpdm_t e;
2536 
2537     fs = calloc(sizeof(struct mpdm_file), 1);
2538 
2539     fs->sock      = -1;
2540     fs->is_pipe   = 0;
2541     fs->skip_wait = 0;
2542     fs->in        = f;
2543     fs->out       = f;
2544 
2545     /* default I/O functions */
2546     fs->f_read = read_auto;
2547     fs->f_write = write_wcs;
2548 
2549 #ifdef CONFOPT_ICONV
2550     /* no iconv encodings by default */
2551     fs->ic_enc = fs->ic_dec = (iconv_t) - 1;
2552 #endif
2553 
2554     /* autochomp? */
2555     fs->auto_chomp = mpdm_is_true(mpdm_get_wcs(mpdm_root(), L"AUTO_CHOMP"));
2556 
2557     v = mpdm_new(MPDM_TYPE_FILE, fs, sizeof(struct mpdm_file));
2558 
2559     e = mpdm_get_wcs(mpdm_root(), L"ENCODING");
2560 
2561     if (mpdm_size(e) == 0)
2562         e = mpdm_get_wcs(mpdm_root(), L"TEMP_ENCODING");
2563 
2564     if (mpdm_size(e)) {
2565         wchar_t *enc = mpdm_string(e);
2566 
2567         if (wcscmp(enc, L"utf-8") == 0) {
2568             fs->f_read = read_utf8_bom;
2569             fs->f_write = write_utf8;
2570         }
2571         else
2572         if (wcscmp(enc, L"utf-8bom") == 0) {
2573             fs->f_read = read_utf8_bom;
2574             fs->f_write = write_utf8_bom;
2575         }
2576         else
2577         if (wcscmp(enc, L"iso8859-1") == 0 ||
2578                  wcscmp(enc, L"8bit") == 0) {
2579             fs->f_read = read_iso8859_1;
2580             fs->f_write = write_iso8859_1;
2581         }
2582         else
2583         if (wcscmp(enc, L"utf-16le") == 0) {
2584             fs->f_read = read_utf16le;
2585             fs->f_write = write_utf16le_bom;
2586         }
2587         else
2588         if (wcscmp(enc, L"utf-16be") == 0) {
2589             fs->f_read = read_utf16be;
2590             fs->f_write = write_utf16be_bom;
2591         }
2592         else
2593         if (wcscmp(enc, L"utf-16") == 0) {
2594             fs->f_read = read_utf16;
2595             fs->f_write = write_utf16le_bom;
2596         }
2597         else
2598         if (wcscmp(enc, L"utf-32le") == 0) {
2599             fs->f_read = read_utf32le;
2600             fs->f_write = write_utf32le_bom;
2601         }
2602         else
2603         if (wcscmp(enc, L"utf-32be") == 0) {
2604             fs->f_read = read_utf32be;
2605             fs->f_write = write_utf32be_bom;
2606         }
2607         else
2608         if (wcscmp(enc, L"utf-32") == 0) {
2609             fs->f_read = read_utf32;
2610             fs->f_write = write_utf32le_bom;
2611         }
2612         else
2613         if (wcscmp(enc, L"msdos-437") == 0) {
2614             fs->f_read = read_msdos_437;
2615             fs->f_write = write_msdos_437;
2616         }
2617         else
2618         if (wcscmp(enc, L"msdos-850") == 0) {
2619             fs->f_read = read_msdos_850;
2620             fs->f_write = write_msdos_850;
2621         }
2622         else
2623         if (wcscmp(enc, L"windows-1252") == 0) {
2624             fs->f_read = read_windows_1252;
2625             fs->f_write = write_windows_1252;
2626         }
2627         else {
2628 #ifdef CONFOPT_ICONV
2629             mpdm_t cs = mpdm_ref(MPDM_2MBS(e->data));
2630 
2631             if ((fs->ic_enc = iconv_open((char *) cs->data, "WCHAR_T")) != (iconv_t) - 1 &&
2632                 (fs->ic_dec = iconv_open("WCHAR_T", (char *) cs->data)) != (iconv_t) - 1) {
2633 
2634                 fs->f_read  = read_iconv;
2635                 fs->f_write = write_iconv;
2636             }
2637 
2638             mpdm_unref(cs);
2639 #endif                          /* CONFOPT_ICONV */
2640         }
2641 
2642         mpdm_set_wcs(mpdm_root(), NULL, L"TEMP_ENCODING");
2643     }
2644 
2645     return v;
2646 }
2647 
2648 
2649 /**
2650  * mpdm_close - Closes a file descriptor.
2651  * @fd: the value containing the file descriptor
2652  *
2653  * Closes the file descriptor.
2654  * [File Management]
2655  */
mpdm_close(mpdm_t fd)2656 int mpdm_close(mpdm_t fd)
2657 {
2658     int r;
2659 
2660     mpdm_ref(fd);
2661     r = file_close(fd);
2662     mpdm_unref(fd);
2663 
2664     return r;
2665 }
2666 
2667 
2668 /** type vc **/
2669 
vc_file_exec(mpdm_t c,mpdm_t args,mpdm_t ctxt)2670 static mpdm_t vc_file_exec(mpdm_t c, mpdm_t args, mpdm_t ctxt)
2671 {
2672     mpdm_t r = NULL;
2673 
2674     /* if there are any arguments, treat as "write" */
2675     if (mpdm_size(args)) {
2676         int n;
2677 
2678         for (n = 0; n < mpdm_size(args); n++)
2679             mpdm_write(c, mpdm_get_i(args, n));
2680 
2681         r = NULL;
2682     }
2683     else
2684         r = mpdm_read(c);
2685 
2686     return r;
2687 }
2688 
vc_file_iterator(mpdm_t set,int * context,mpdm_t * v,mpdm_t * i)2689 static int vc_file_iterator(mpdm_t set, int *context, mpdm_t *v, mpdm_t *i)
2690 {
2691     mpdm_t w = mpdm_read(set);
2692     int ret  = 0;
2693 
2694     if (w != NULL) {
2695         if (v)
2696             *v = w;
2697         else
2698             mpdm_void(w);
2699 
2700         if (i) *i = MPDM_I(*context);
2701 
2702         (*context)++;
2703         ret = 1;
2704     }
2705 
2706     return ret;
2707 }
2708 
2709 
2710 struct mpdm_type_vc mpdm_vc_file = { /* VC */
2711     L"file",                /* name */
2712     vc_file_destroy,        /* destroy */
2713     vc_default_is_true,     /* is_true */
2714     vc_default_count,       /* count */
2715     vc_default_get_i,       /* get_i */
2716     vc_default_get,         /* get */
2717     vc_default_string,      /* string */
2718     vc_default_del_i,       /* del_i */
2719     vc_default_del,         /* del */
2720     vc_default_set_i,       /* set_i */
2721     vc_default_set,         /* set */
2722     vc_file_exec,           /* exec */
2723     vc_file_iterator,       /* iterator */
2724     vc_default_map          /* map */
2725 };
2726