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