This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Current ppport.h forcibly overrides older buggy versions of utf8_to_uvchr_buf
[perl5.git] / cygwin / cygwin.c
1 /*
2  * Cygwin extras
3  */
4
5 #define PERLIO_NOT_STDIO 0
6 #include "EXTERN.h"
7 #include "perl.h"
8 #undef USE_DYNAMIC_LOADING
9 #include "XSUB.h"
10
11 #include <unistd.h>
12 #include <process.h>
13 #include <sys/cygwin.h>
14 #include <cygwin/version.h>
15 #include <mntent.h>
16 #include <alloca.h>
17 #include <dlfcn.h>
18 #if (CYGWIN_VERSION_API_MINOR >= 181)
19 #include <wchar.h>
20 #endif
21
22 /*
23  * pp_system() implemented via spawn()
24  * - more efficient and useful when embedding Perl in non-Cygwin apps
25  * - code mostly borrowed from djgpp.c
26  */
27 static int
28 do_spawnvp (const char *path, const char * const *argv)
29 {
30     dTHX;
31     Sigsave_t ihand,qhand;
32     int childpid, result, status;
33
34     rsignal_save(SIGINT, (Sighandler_t) SIG_IGN, &ihand);
35     rsignal_save(SIGQUIT, (Sighandler_t) SIG_IGN, &qhand);
36     childpid = spawnvp(_P_NOWAIT,path,argv);
37     if (childpid < 0) {
38         status = -1;
39         if(ckWARN(WARN_EXEC))
40             Perl_warner(aTHX_ packWARN(WARN_EXEC),"Can't spawn \"%s\": %s",
41                     path,Strerror (errno));
42     } else {
43         do {
44             result = wait4pid(childpid, &status, 0);
45         } while (result == -1 && errno == EINTR);
46         if(result < 0)
47             status = -1;
48     }
49     (void)rsignal_restore(SIGINT, &ihand);
50     (void)rsignal_restore(SIGQUIT, &qhand);
51     return status;
52 }
53
54 int
55 do_aspawn (SV *really, void **mark, void **sp)
56 {
57     dTHX;
58     int  rc;
59     char const **a;
60     char *tmps,**argv;
61     STRLEN n_a;
62
63     if (sp<=mark)
64         return -1;
65     argv=(char**) alloca ((sp-mark+3)*sizeof (char*));
66     a=(char const **)argv;
67
68     while (++mark <= sp)
69         if (*mark)
70             *a++ = SvPVx((SV *)*mark, n_a);
71         else
72             *a++ = "";
73     *a = (char*)NULL;
74
75     if (argv[0][0] != '/' && argv[0][0] != '\\'
76         && !(argv[0][0] && argv[0][1] == ':'
77         && (argv[0][2] == '/' || argv[0][2] != '\\'))
78      ) /* will swawnvp use PATH? */
79          TAINT_ENV();   /* testing IFS here is overkill, probably */
80
81     if (really && *(tmps = SvPV(really, n_a)))
82         rc=do_spawnvp (tmps,(const char * const *)argv);
83     else
84         rc=do_spawnvp (argv[0],(const char *const *)argv);
85
86     return rc;
87 }
88
89 int
90 do_spawn (char *cmd)
91 {
92     dTHX;
93     char const **argv, **a;
94     char *s;
95     char const *metachars = "$&*(){}[]'\";\\?>|<~`\n";
96     const char *command[4];
97     int result;
98
99     ENTER;
100     while (*cmd && isSPACE(*cmd))
101         cmd++;
102
103     if (strBEGINs (cmd,"/bin/sh") && isSPACE (cmd[7]))
104         cmd+=5;
105
106     /* save an extra exec if possible */
107     /* see if there are shell metacharacters in it */
108     if (strstr (cmd,"..."))
109         goto doshell;
110     if (*cmd=='.' && isSPACE (cmd[1]))
111         goto doshell;
112     if (strBEGINs (cmd,"exec") && isSPACE (cmd[4]))
113         goto doshell;
114     for (s=cmd; *s && isALPHA (*s); s++) ;      /* catch VAR=val gizmo */
115     if (*s=='=')
116         goto doshell;
117
118     for (s=cmd; *s; s++)
119         if (strchr (metachars,*s))
120         {
121             if (*s=='\n' && s[1]=='\0')
122             {
123                 *s='\0';
124                 break;
125             }
126         doshell:
127             command[0] = "sh";
128             command[1] = "-c";
129             command[2] = cmd;
130             command[3] = NULL;
131
132             result = do_spawnvp("sh",command);
133             goto leave;
134         }
135
136     Newx (argv, (s-cmd)/2+2, const char*);
137     SAVEFREEPV(argv);
138     cmd=savepvn (cmd,s-cmd);
139     SAVEFREEPV(cmd);
140     a=argv;
141     for (s=cmd; *s;) {
142         while (*s && isSPACE (*s)) s++;
143         if (*s)
144             *(a++)=s;
145         while (*s && !isSPACE (*s)) s++;
146         if (*s)
147             *s++='\0';
148     }
149     *a = (char*)NULL;
150     if (!argv[0])
151         result = -1;
152     else
153         result = do_spawnvp(argv[0],(const char * const *)argv);
154 leave:
155     LEAVE;
156     return result;
157 }
158
159 #if (CYGWIN_VERSION_API_MINOR >= 181)
160 char*
161 wide_to_utf8(const wchar_t *wbuf)
162 {
163     char *buf;
164     int wlen = 0;
165     char *oldlocale;
166
167     /* Here and elsewhere in this file, we have a critical section to prevent
168      * another thread from changing the locale out from under us.  XXX But why
169      * not just use uvchr_to_utf8? */
170     SETLOCALE_LOCK;
171
172     oldlocale = setlocale(LC_CTYPE, NULL);
173     setlocale(LC_CTYPE, "utf-8");
174
175     /* uvchr_to_utf8(buf, chr) or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */
176     wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
177     buf = (char *) safemalloc(wlen+1);
178     wcsrtombs(buf, (const wchar_t **)&wbuf, wlen, NULL);
179
180     if (oldlocale) setlocale(LC_CTYPE, oldlocale);
181     else setlocale(LC_CTYPE, "C");
182
183     SETLOCALE_UNLOCK;
184
185     return buf;
186 }
187
188 wchar_t*
189 utf8_to_wide(const char *buf)
190 {
191     wchar_t *wbuf;
192     mbstate_t mbs;
193     char *oldlocale;
194     int wlen = sizeof(wchar_t)*strlen(buf);
195
196     SETLOCALE_LOCK;
197
198     oldlocale = setlocale(LC_CTYPE, NULL);
199
200     setlocale(LC_CTYPE, "utf-8");
201     wbuf = (wchar_t *) safemalloc(wlen);
202     /* utf8_to_uvchr_buf(pathname, pathname + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
203     wlen = mbsrtowcs(wbuf, (const char**)&buf, wlen, &mbs);
204
205     if (oldlocale) setlocale(LC_CTYPE, oldlocale);
206     else setlocale(LC_CTYPE, "C");
207
208     SETLOCALE_UNLOCK;
209
210     return wbuf;
211 }
212 #endif /* cygwin 1.7 */
213
214 /* see also Cwd.pm */
215 XS(Cygwin_cwd)
216 {
217     dXSARGS;
218     char *cwd;
219
220     /* See https://github.com/Perl/perl5/issues/8345
221        There is Cwd->cwd() usage in the wild, and previous versions didn't die.
222      */
223     if(items > 1)
224         Perl_croak(aTHX_ "Usage: Cwd::cwd()");
225     if((cwd = getcwd(NULL, -1))) {
226         ST(0) = sv_2mortal(newSVpv(cwd, 0));
227         free(cwd);
228         SvTAINTED_on(ST(0));
229         XSRETURN(1);
230     }
231     XSRETURN_UNDEF;
232 }
233
234 XS(XS_Cygwin_pid_to_winpid)
235 {
236     dXSARGS;
237     dXSTARG;
238     pid_t pid, RETVAL;
239
240     if (items != 1)
241         Perl_croak(aTHX_ "Usage: Cygwin::pid_to_winpid(pid)");
242
243     pid = (pid_t)SvIV(ST(0));
244
245     if ((RETVAL = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, pid)) > 0) {
246         XSprePUSH; PUSHi((IV)RETVAL);
247         XSRETURN(1);
248     }
249     XSRETURN_UNDEF;
250 }
251
252 XS(XS_Cygwin_winpid_to_pid)
253 {
254     dXSARGS;
255     dXSTARG;
256     pid_t pid, RETVAL;
257
258     if (items != 1)
259         Perl_croak(aTHX_ "Usage: Cygwin::winpid_to_pid(pid)");
260
261     pid = (pid_t)SvIV(ST(0));
262
263 #if (CYGWIN_VERSION_API_MINOR >= 181)
264     RETVAL = cygwin_winpid_to_pid(pid);
265 #else
266     RETVAL = cygwin32_winpid_to_pid(pid);
267 #endif
268     if (RETVAL > 0) {
269         XSprePUSH; PUSHi((IV)RETVAL);
270         XSRETURN(1);
271     }
272     XSRETURN_UNDEF;
273 }
274
275 XS(XS_Cygwin_win_to_posix_path)
276
277 {
278     dXSARGS;
279     int absolute_flag = 0;
280     STRLEN len;
281     int err = 0;
282     char *src_path;
283     char *posix_path;
284     int isutf8 = 0;
285
286     if (items < 1 || items > 2)
287         Perl_croak(aTHX_ "Usage: Cygwin::win_to_posix_path(pathname, [absolute])");
288
289     src_path = SvPV(ST(0), len);
290     if (items == 2)
291         absolute_flag = SvTRUE(ST(1));
292
293     if (!len)
294         Perl_croak(aTHX_ "can't convert empty path");
295     isutf8 = SvUTF8(ST(0));
296
297 #if (CYGWIN_VERSION_API_MINOR >= 181)
298     /* Check utf8 flag and use wide api then.
299        Size calculation: On overflow let cygwin_conv_path calculate the final size.
300      */
301     if (isutf8) {
302         int what = absolute_flag ? CCP_WIN_W_TO_POSIX : CCP_WIN_W_TO_POSIX | CCP_RELATIVE;
303         STRLEN wlen = sizeof(wchar_t)*(len + 260 + 1001);
304         wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len);
305         wchar_t *wbuf = (wchar_t *) safemalloc(wlen);
306         if (!IN_BYTES) {
307             mbstate_t mbs;
308             char *oldlocale;
309
310             SETLOCALE_LOCK;
311
312             oldlocale = setlocale(LC_CTYPE, NULL);
313             setlocale(LC_CTYPE, "utf-8");
314             /* utf8_to_uvchr_buf(src_path, src_path + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
315             wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs);
316             if (wlen > 0)
317                 err = cygwin_conv_path(what, wpath, wbuf, wlen);
318             if (oldlocale) setlocale(LC_CTYPE, oldlocale);
319             else setlocale(LC_CTYPE, "C");
320
321             SETLOCALE_UNLOCK;
322         } else { /* use bytes; assume already ucs-2 encoded bytestream */
323             err = cygwin_conv_path(what, src_path, wbuf, wlen);
324         }
325         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
326             int newlen = cygwin_conv_path(what, wpath, wbuf, 0);
327             wbuf = (wchar_t *) realloc(&wbuf, newlen);
328             err = cygwin_conv_path(what, wpath, wbuf, newlen);
329             wlen = newlen;
330         }
331         /* utf16_to_utf8(*p, *d, bytlen, *newlen) */
332         posix_path = (char *) safemalloc(wlen*3);
333         Perl_utf16_to_utf8(aTHX_ (U8*)&wpath, (U8*)posix_path, wlen*2, &len);
334         /*
335         wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
336         posix_path = (char *) safemalloc(wlen+1);
337         wcsrtombs(posix_path, (const wchar_t **)&wbuf, wlen, NULL);
338         */
339     } else {
340         int what = absolute_flag ? CCP_WIN_A_TO_POSIX : CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
341         posix_path = (char *) safemalloc (len + 260 + 1001);
342         err = cygwin_conv_path(what, src_path, posix_path, len + 260 + 1001);
343         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
344             int newlen = cygwin_conv_path(what, src_path, posix_path, 0);
345             posix_path = (char *) realloc(&posix_path, newlen);
346             err = cygwin_conv_path(what, src_path, posix_path, newlen);
347         }
348     }
349 #else
350     posix_path = (char *) safemalloc (len + 260 + 1001);
351     if (absolute_flag)
352         err = cygwin_conv_to_full_posix_path(src_path, posix_path);
353     else
354         err = cygwin_conv_to_posix_path(src_path, posix_path);
355 #endif
356     if (!err) {
357         EXTEND(SP, 1);
358         ST(0) = sv_2mortal(newSVpv(posix_path, 0));
359         if (isutf8) { /* src was utf-8, so result should also */
360             /* TODO: convert ANSI (local windows encoding) to utf-8 on cygwin-1.5 */
361             SvUTF8_on(ST(0));
362         }
363         safefree(posix_path);
364         XSRETURN(1);
365     } else {
366         safefree(posix_path);
367         XSRETURN_UNDEF;
368     }
369 }
370
371 XS(XS_Cygwin_posix_to_win_path)
372 {
373     dXSARGS;
374     int absolute_flag = 0;
375     STRLEN len;
376     int err = 0;
377     char *src_path, *win_path;
378     int isutf8 = 0;
379
380     if (items < 1 || items > 2)
381         Perl_croak(aTHX_ "Usage: Cygwin::posix_to_win_path(pathname, [absolute])");
382
383     src_path = SvPVx(ST(0), len);
384     if (items == 2)
385         absolute_flag = SvTRUE(ST(1));
386
387     if (!len)
388         Perl_croak(aTHX_ "can't convert empty path");
389     isutf8 = SvUTF8(ST(0));
390 #if (CYGWIN_VERSION_API_MINOR >= 181)
391     /* Check utf8 flag and use wide api then.
392        Size calculation: On overflow let cygwin_conv_path calculate the final size.
393      */
394     if (isutf8) {
395         int what = absolute_flag ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_W | CCP_RELATIVE;
396         int wlen = sizeof(wchar_t)*(len + 260 + 1001);
397         wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len);
398         wchar_t *wbuf = (wchar_t *) safemalloc(wlen);
399         char *oldlocale;
400
401         SETLOCALE_LOCK;
402
403         oldlocale = setlocale(LC_CTYPE, NULL);
404         setlocale(LC_CTYPE, "utf-8");
405         if (!IN_BYTES) {
406             mbstate_t mbs;
407             /* utf8_to_uvchr_buf(src_path, src_path + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
408             wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs);
409             if (wlen > 0)
410                 err = cygwin_conv_path(what, wpath, wbuf, wlen);
411         } else { /* use bytes; assume already ucs-2 encoded bytestream */
412             err = cygwin_conv_path(what, src_path, wbuf, wlen);
413         }
414         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
415             int newlen = cygwin_conv_path(what, wpath, wbuf, 0);
416             wbuf = (wchar_t *) realloc(&wbuf, newlen);
417             err = cygwin_conv_path(what, wpath, wbuf, newlen);
418             wlen = newlen;
419         }
420         /* also see utf8.c: Perl_utf16_to_utf8() or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */
421         wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
422         win_path = (char *) safemalloc(wlen+1);
423         wcsrtombs(win_path, (const wchar_t **)&wbuf, wlen, NULL);
424         if (oldlocale) setlocale(LC_CTYPE, oldlocale);
425         else setlocale(LC_CTYPE, "C");
426
427         SETLOCALE_UNLOCK;
428     } else {
429         int what = absolute_flag ? CCP_POSIX_TO_WIN_A : CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
430         win_path = (char *) safemalloc(len + 260 + 1001);
431         err = cygwin_conv_path(what, src_path, win_path, len + 260 + 1001);
432         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
433             int newlen = cygwin_conv_path(what, src_path, win_path, 0);
434             win_path = (char *) realloc(&win_path, newlen);
435             err = cygwin_conv_path(what, src_path, win_path, newlen);
436         }
437     }
438 #else
439     if (isutf8)
440         Perl_warn(aTHX_ "can't convert utf8 path");
441     win_path = (char *) safemalloc(len + 260 + 1001);
442     if (absolute_flag)
443         err = cygwin_conv_to_full_win32_path(src_path, win_path);
444     else
445         err = cygwin_conv_to_win32_path(src_path, win_path);
446 #endif
447     if (!err) {
448         EXTEND(SP, 1);
449         ST(0) = sv_2mortal(newSVpv(win_path, 0));
450         if (isutf8) {
451             SvUTF8_on(ST(0));
452         }
453         safefree(win_path);
454         XSRETURN(1);
455     } else {
456         safefree(win_path);
457         XSRETURN_UNDEF;
458     }
459 }
460
461 XS(XS_Cygwin_mount_table)
462 {
463     dXSARGS;
464     struct mntent *mnt;
465
466     if (items != 0)
467         Perl_croak(aTHX_ "Usage: Cygwin::mount_table");
468     /* => array of [mnt_dir mnt_fsname mnt_type mnt_opts] */
469
470     setmntent (0, 0);
471     while ((mnt = getmntent (0))) {
472         AV* av = newAV();
473         av_push(av, newSVpvn(mnt->mnt_dir, strlen(mnt->mnt_dir)));
474         av_push(av, newSVpvn(mnt->mnt_fsname, strlen(mnt->mnt_fsname)));
475         av_push(av, newSVpvn(mnt->mnt_type, strlen(mnt->mnt_type)));
476         av_push(av, newSVpvn(mnt->mnt_opts, strlen(mnt->mnt_opts)));
477         XPUSHs(sv_2mortal(newRV_noinc((SV*)av)));
478     }
479     endmntent (0);
480     PUTBACK;
481 }
482
483 XS(XS_Cygwin_mount_flags)
484 {
485     dXSARGS;
486     char *pathname;
487     char flags[PATH_MAX];
488     flags[0] = '\0';
489
490     if (items != 1)
491         Perl_croak(aTHX_ "Usage: Cygwin::mount_flags( mnt_dir | '/cygdrive' )");
492
493     pathname = SvPV_nolen(ST(0));
494
495     if (strEQ(pathname, "/cygdrive")) {
496         char user[PATH_MAX];
497         char system[PATH_MAX];
498         char user_flags[PATH_MAX];
499         char system_flags[PATH_MAX];
500
501         cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system,
502                          user_flags, system_flags);
503
504         if (strlen(user) > 0) {
505             sprintf(flags, "%s,cygdrive,%s", user_flags, user);
506         } else {
507             sprintf(flags, "%s,cygdrive,%s", system_flags, system);
508         }
509
510         ST(0) = sv_2mortal(newSVpv(flags, 0));
511         XSRETURN(1);
512
513     } else {
514         struct mntent *mnt;
515         int found = 0;
516         setmntent (0, 0);
517         while ((mnt = getmntent (0))) {
518             if (strEQ(pathname, mnt->mnt_dir)) {
519                 strcpy(flags, mnt->mnt_type);
520                 if (strlen(mnt->mnt_opts) > 0) {
521                     strcat(flags, ",");
522                     strcat(flags, mnt->mnt_opts);
523                 }
524                 found++;
525                 break;
526             }
527         }
528         endmntent (0);
529
530         /* Check if arg is the current volume moint point if not default,
531          * and then use CW_GET_CYGDRIVE_INFO also.
532          */
533         if (!found) {
534             char user[PATH_MAX];
535             char system[PATH_MAX];
536             char user_flags[PATH_MAX];
537             char system_flags[PATH_MAX];
538
539             cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system,
540                              user_flags, system_flags);
541
542             if (strlen(user) > 0) {
543                 if (strNE(user,pathname)) {
544                     sprintf(flags, "%s,cygdrive,%s", user_flags, user);
545                     found++;
546                 }
547             } else {
548                 if (strNE(user,pathname)) {
549                     sprintf(flags, "%s,cygdrive,%s", system_flags, system);
550                     found++;
551                 }
552             }
553         }
554         if (found) {
555             ST(0) = sv_2mortal(newSVpv(flags, 0));
556             XSRETURN(1);
557         } else {
558             XSRETURN_UNDEF;
559         }
560     }
561 }
562
563 XS(XS_Cygwin_is_binmount)
564 {
565     dXSARGS;
566     char *pathname;
567
568     if (items != 1)
569         Perl_croak(aTHX_ "Usage: Cygwin::is_binmount(pathname)");
570
571     pathname = SvPV_nolen(ST(0));
572
573     ST(0) = boolSV(cygwin_internal(CW_GET_BINMODE, pathname));
574     XSRETURN(1);
575 }
576
577 XS(XS_Cygwin_sync_winenv){ cygwin_internal(CW_SYNC_WINENV); }
578
579 void
580 init_os_extras(void)
581 {
582     dTHX;
583     char const *file = __FILE__;
584     void *handle;
585
586     newXS("Cwd::cwd", Cygwin_cwd, file);
587     newXSproto("Cygwin::winpid_to_pid", XS_Cygwin_winpid_to_pid, file, "$");
588     newXSproto("Cygwin::pid_to_winpid", XS_Cygwin_pid_to_winpid, file, "$");
589     newXSproto("Cygwin::win_to_posix_path", XS_Cygwin_win_to_posix_path, file, "$;$");
590     newXSproto("Cygwin::posix_to_win_path", XS_Cygwin_posix_to_win_path, file, "$;$");
591     newXSproto("Cygwin::mount_table", XS_Cygwin_mount_table, file, "");
592     newXSproto("Cygwin::mount_flags", XS_Cygwin_mount_flags, file, "$");
593     newXSproto("Cygwin::is_binmount", XS_Cygwin_is_binmount, file, "$");
594     newXS("Cygwin::sync_winenv", XS_Cygwin_sync_winenv, file);
595
596     /* Initialize Win32CORE if it has been statically linked. */
597     handle = dlopen(NULL, RTLD_LAZY);
598     if (handle) {
599         void (*pfn_init)(pTHX);
600         pfn_init = (void (*)(pTHX))dlsym(handle, "init_Win32CORE");
601         if (pfn_init)
602             pfn_init(aTHX);
603         dlclose(handle);
604     }
605 }