This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Don't use POSIX 2008 locales on cygwin
[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     dTHX;
164     char *buf;
165     int wlen = 0;
166     char *oldlocale;
167
168     /* Here and elsewhere in this file, we have a critical section to prevent
169      * another thread from changing the locale out from under us.  XXX But why
170      * not just use uvchr_to_utf8? */
171     SETLOCALE_LOCK;
172
173     oldlocale = setlocale(LC_CTYPE, NULL);
174     setlocale(LC_CTYPE, "utf-8");
175
176     /* uvchr_to_utf8(buf, chr) or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */
177     wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
178     buf = (char *) safemalloc(wlen+1);
179     wcsrtombs(buf, (const wchar_t **)&wbuf, wlen, NULL);
180
181     if (oldlocale) setlocale(LC_CTYPE, oldlocale);
182     else setlocale(LC_CTYPE, "C");
183
184     SETLOCALE_UNLOCK;
185
186     return buf;
187 }
188
189 wchar_t*
190 utf8_to_wide(const char *buf)
191 {
192     dTHX;
193     wchar_t *wbuf;
194     mbstate_t mbs;
195     char *oldlocale;
196     int wlen = sizeof(wchar_t)*strlen(buf);
197
198     SETLOCALE_LOCK;
199
200     oldlocale = setlocale(LC_CTYPE, NULL);
201
202     setlocale(LC_CTYPE, "utf-8");
203     wbuf = (wchar_t *) safemalloc(wlen);
204     /* utf8_to_uvchr_buf(pathname, pathname + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
205     wlen = mbsrtowcs(wbuf, (const char**)&buf, wlen, &mbs);
206
207     if (oldlocale) setlocale(LC_CTYPE, oldlocale);
208     else setlocale(LC_CTYPE, "C");
209
210     SETLOCALE_UNLOCK;
211
212     return wbuf;
213 }
214 #endif /* cygwin 1.7 */
215
216 /* see also Cwd.pm */
217 XS(Cygwin_cwd)
218 {
219     dXSARGS;
220     char *cwd;
221
222     /* See https://github.com/Perl/perl5/issues/8345
223        There is Cwd->cwd() usage in the wild, and previous versions didn't die.
224      */
225     if(items > 1)
226         Perl_croak(aTHX_ "Usage: Cwd::cwd()");
227     if((cwd = getcwd(NULL, -1))) {
228         ST(0) = sv_2mortal(newSVpv(cwd, 0));
229         free(cwd);
230         SvTAINTED_on(ST(0));
231         XSRETURN(1);
232     }
233     XSRETURN_UNDEF;
234 }
235
236 XS(XS_Cygwin_pid_to_winpid)
237 {
238     dXSARGS;
239     dXSTARG;
240     pid_t pid, RETVAL;
241
242     if (items != 1)
243         Perl_croak(aTHX_ "Usage: Cygwin::pid_to_winpid(pid)");
244
245     pid = (pid_t)SvIV(ST(0));
246
247     if ((RETVAL = cygwin_internal(CW_CYGWIN_PID_TO_WINPID, pid)) > 0) {
248         XSprePUSH; PUSHi((IV)RETVAL);
249         XSRETURN(1);
250     }
251     XSRETURN_UNDEF;
252 }
253
254 XS(XS_Cygwin_winpid_to_pid)
255 {
256     dXSARGS;
257     dXSTARG;
258     pid_t pid, RETVAL;
259
260     if (items != 1)
261         Perl_croak(aTHX_ "Usage: Cygwin::winpid_to_pid(pid)");
262
263     pid = (pid_t)SvIV(ST(0));
264
265 #if (CYGWIN_VERSION_API_MINOR >= 181)
266     RETVAL = cygwin_winpid_to_pid(pid);
267 #else
268     RETVAL = cygwin32_winpid_to_pid(pid);
269 #endif
270     if (RETVAL > 0) {
271         XSprePUSH; PUSHi((IV)RETVAL);
272         XSRETURN(1);
273     }
274     XSRETURN_UNDEF;
275 }
276
277 XS(XS_Cygwin_win_to_posix_path)
278
279 {
280     dXSARGS;
281     int absolute_flag = 0;
282     STRLEN len;
283     int err = 0;
284     char *src_path;
285     char *posix_path;
286     int isutf8 = 0;
287
288     if (items < 1 || items > 2)
289         Perl_croak(aTHX_ "Usage: Cygwin::win_to_posix_path(pathname, [absolute])");
290
291     src_path = SvPV(ST(0), len);
292     if (items == 2)
293         absolute_flag = SvTRUE(ST(1));
294
295     if (!len)
296         Perl_croak(aTHX_ "can't convert empty path");
297     isutf8 = SvUTF8(ST(0));
298
299 #if (CYGWIN_VERSION_API_MINOR >= 181)
300     /* Check utf8 flag and use wide api then.
301        Size calculation: On overflow let cygwin_conv_path calculate the final size.
302      */
303     if (isutf8) {
304         int what = absolute_flag ? CCP_WIN_W_TO_POSIX : CCP_WIN_W_TO_POSIX | CCP_RELATIVE;
305         STRLEN wlen = sizeof(wchar_t)*(len + 260 + 1001);
306         wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len);
307         wchar_t *wbuf = (wchar_t *) safemalloc(wlen);
308         if (!IN_BYTES) {
309             mbstate_t mbs;
310             char *oldlocale;
311
312             SETLOCALE_LOCK;
313
314             oldlocale = setlocale(LC_CTYPE, NULL);
315             setlocale(LC_CTYPE, "utf-8");
316             /* utf8_to_uvchr_buf(src_path, src_path + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
317             wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs);
318             if (wlen > 0)
319                 err = cygwin_conv_path(what, wpath, wbuf, wlen);
320             if (oldlocale) setlocale(LC_CTYPE, oldlocale);
321             else setlocale(LC_CTYPE, "C");
322
323             SETLOCALE_UNLOCK;
324         } else { /* use bytes; assume already ucs-2 encoded bytestream */
325             err = cygwin_conv_path(what, src_path, wbuf, wlen);
326         }
327         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
328             int newlen = cygwin_conv_path(what, wpath, wbuf, 0);
329             wbuf = (wchar_t *) realloc(&wbuf, newlen);
330             err = cygwin_conv_path(what, wpath, wbuf, newlen);
331             wlen = newlen;
332         }
333         /* utf16_to_utf8(*p, *d, bytlen, *newlen) */
334         posix_path = (char *) safemalloc(wlen*3);
335         Perl_utf16_to_utf8(aTHX_ (U8*)&wpath, (U8*)posix_path, wlen*2, &len);
336         /*
337         wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
338         posix_path = (char *) safemalloc(wlen+1);
339         wcsrtombs(posix_path, (const wchar_t **)&wbuf, wlen, NULL);
340         */
341     } else {
342         int what = absolute_flag ? CCP_WIN_A_TO_POSIX : CCP_WIN_A_TO_POSIX | CCP_RELATIVE;
343         posix_path = (char *) safemalloc (len + 260 + 1001);
344         err = cygwin_conv_path(what, src_path, posix_path, len + 260 + 1001);
345         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
346             int newlen = cygwin_conv_path(what, src_path, posix_path, 0);
347             posix_path = (char *) realloc(&posix_path, newlen);
348             err = cygwin_conv_path(what, src_path, posix_path, newlen);
349         }
350     }
351 #else
352     posix_path = (char *) safemalloc (len + 260 + 1001);
353     if (absolute_flag)
354         err = cygwin_conv_to_full_posix_path(src_path, posix_path);
355     else
356         err = cygwin_conv_to_posix_path(src_path, posix_path);
357 #endif
358     if (!err) {
359         EXTEND(SP, 1);
360         ST(0) = sv_2mortal(newSVpv(posix_path, 0));
361         if (isutf8) { /* src was utf-8, so result should also */
362             /* TODO: convert ANSI (local windows encoding) to utf-8 on cygwin-1.5 */
363             SvUTF8_on(ST(0));
364         }
365         safefree(posix_path);
366         XSRETURN(1);
367     } else {
368         safefree(posix_path);
369         XSRETURN_UNDEF;
370     }
371 }
372
373 XS(XS_Cygwin_posix_to_win_path)
374 {
375     dXSARGS;
376     int absolute_flag = 0;
377     STRLEN len;
378     int err = 0;
379     char *src_path, *win_path;
380     int isutf8 = 0;
381
382     if (items < 1 || items > 2)
383         Perl_croak(aTHX_ "Usage: Cygwin::posix_to_win_path(pathname, [absolute])");
384
385     src_path = SvPVx(ST(0), len);
386     if (items == 2)
387         absolute_flag = SvTRUE(ST(1));
388
389     if (!len)
390         Perl_croak(aTHX_ "can't convert empty path");
391     isutf8 = SvUTF8(ST(0));
392 #if (CYGWIN_VERSION_API_MINOR >= 181)
393     /* Check utf8 flag and use wide api then.
394        Size calculation: On overflow let cygwin_conv_path calculate the final size.
395      */
396     if (isutf8) {
397         int what = absolute_flag ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_W | CCP_RELATIVE;
398         int wlen = sizeof(wchar_t)*(len + 260 + 1001);
399         wchar_t *wpath = (wchar_t *) safemalloc(sizeof(wchar_t)*len);
400         wchar_t *wbuf = (wchar_t *) safemalloc(wlen);
401         char *oldlocale;
402
403         SETLOCALE_LOCK;
404
405         oldlocale = setlocale(LC_CTYPE, NULL);
406         setlocale(LC_CTYPE, "utf-8");
407         if (!IN_BYTES) {
408             mbstate_t mbs;
409             /* utf8_to_uvchr_buf(src_path, src_path + wlen, wpath) or Encoding::_utf8_to_bytes(sv, "UCS-2BE"); */
410             wlen = mbsrtowcs(wpath, (const char**)&src_path, wlen, &mbs);
411             if (wlen > 0)
412                 err = cygwin_conv_path(what, wpath, wbuf, wlen);
413         } else { /* use bytes; assume already ucs-2 encoded bytestream */
414             err = cygwin_conv_path(what, src_path, wbuf, wlen);
415         }
416         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
417             int newlen = cygwin_conv_path(what, wpath, wbuf, 0);
418             wbuf = (wchar_t *) realloc(&wbuf, newlen);
419             err = cygwin_conv_path(what, wpath, wbuf, newlen);
420             wlen = newlen;
421         }
422         /* also see utf8.c: Perl_utf16_to_utf8() or Encoding::_bytes_to_utf8(sv, "UCS-2BE"); */
423         wlen = wcsrtombs(NULL, (const wchar_t **)&wbuf, wlen, NULL);
424         win_path = (char *) safemalloc(wlen+1);
425         wcsrtombs(win_path, (const wchar_t **)&wbuf, wlen, NULL);
426         if (oldlocale) setlocale(LC_CTYPE, oldlocale);
427         else setlocale(LC_CTYPE, "C");
428
429         SETLOCALE_UNLOCK;
430     } else {
431         int what = absolute_flag ? CCP_POSIX_TO_WIN_A : CCP_POSIX_TO_WIN_A | CCP_RELATIVE;
432         win_path = (char *) safemalloc(len + 260 + 1001);
433         err = cygwin_conv_path(what, src_path, win_path, len + 260 + 1001);
434         if (err == ENOSPC) { /* our space assumption was wrong, not enough space */
435             int newlen = cygwin_conv_path(what, src_path, win_path, 0);
436             win_path = (char *) realloc(&win_path, newlen);
437             err = cygwin_conv_path(what, src_path, win_path, newlen);
438         }
439     }
440 #else
441     if (isutf8)
442         Perl_warn(aTHX_ "can't convert utf8 path");
443     win_path = (char *) safemalloc(len + 260 + 1001);
444     if (absolute_flag)
445         err = cygwin_conv_to_full_win32_path(src_path, win_path);
446     else
447         err = cygwin_conv_to_win32_path(src_path, win_path);
448 #endif
449     if (!err) {
450         EXTEND(SP, 1);
451         ST(0) = sv_2mortal(newSVpv(win_path, 0));
452         if (isutf8) {
453             SvUTF8_on(ST(0));
454         }
455         safefree(win_path);
456         XSRETURN(1);
457     } else {
458         safefree(win_path);
459         XSRETURN_UNDEF;
460     }
461 }
462
463 XS(XS_Cygwin_mount_table)
464 {
465     dXSARGS;
466     struct mntent *mnt;
467
468     if (items != 0)
469         Perl_croak(aTHX_ "Usage: Cygwin::mount_table");
470     /* => array of [mnt_dir mnt_fsname mnt_type mnt_opts] */
471
472     setmntent (0, 0);
473     while ((mnt = getmntent (0))) {
474         AV* av = newAV();
475         av_push(av, newSVpvn(mnt->mnt_dir, strlen(mnt->mnt_dir)));
476         av_push(av, newSVpvn(mnt->mnt_fsname, strlen(mnt->mnt_fsname)));
477         av_push(av, newSVpvn(mnt->mnt_type, strlen(mnt->mnt_type)));
478         av_push(av, newSVpvn(mnt->mnt_opts, strlen(mnt->mnt_opts)));
479         XPUSHs(sv_2mortal(newRV_noinc((SV*)av)));
480     }
481     endmntent (0);
482     PUTBACK;
483 }
484
485 XS(XS_Cygwin_mount_flags)
486 {
487     dXSARGS;
488     char *pathname;
489     char flags[PATH_MAX];
490     flags[0] = '\0';
491
492     if (items != 1)
493         Perl_croak(aTHX_ "Usage: Cygwin::mount_flags( mnt_dir | '/cygdrive' )");
494
495     pathname = SvPV_nolen(ST(0));
496
497     if (strEQ(pathname, "/cygdrive")) {
498         char user[PATH_MAX];
499         char system[PATH_MAX];
500         char user_flags[PATH_MAX];
501         char system_flags[PATH_MAX];
502
503         cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system,
504                          user_flags, system_flags);
505
506         if (strlen(user) > 0) {
507             sprintf(flags, "%s,cygdrive,%s", user_flags, user);
508         } else {
509             sprintf(flags, "%s,cygdrive,%s", system_flags, system);
510         }
511
512         ST(0) = sv_2mortal(newSVpv(flags, 0));
513         XSRETURN(1);
514
515     } else {
516         struct mntent *mnt;
517         int found = 0;
518         setmntent (0, 0);
519         while ((mnt = getmntent (0))) {
520             if (strEQ(pathname, mnt->mnt_dir)) {
521                 strcpy(flags, mnt->mnt_type);
522                 if (strlen(mnt->mnt_opts) > 0) {
523                     strcat(flags, ",");
524                     strcat(flags, mnt->mnt_opts);
525                 }
526                 found++;
527                 break;
528             }
529         }
530         endmntent (0);
531
532         /* Check if arg is the current volume moint point if not default,
533          * and then use CW_GET_CYGDRIVE_INFO also.
534          */
535         if (!found) {
536             char user[PATH_MAX];
537             char system[PATH_MAX];
538             char user_flags[PATH_MAX];
539             char system_flags[PATH_MAX];
540
541             cygwin_internal (CW_GET_CYGDRIVE_INFO, user, system,
542                              user_flags, system_flags);
543
544             if (strlen(user) > 0) {
545                 if (strNE(user,pathname)) {
546                     sprintf(flags, "%s,cygdrive,%s", user_flags, user);
547                     found++;
548                 }
549             } else {
550                 if (strNE(user,pathname)) {
551                     sprintf(flags, "%s,cygdrive,%s", system_flags, system);
552                     found++;
553                 }
554             }
555         }
556         if (found) {
557             ST(0) = sv_2mortal(newSVpv(flags, 0));
558             XSRETURN(1);
559         } else {
560             XSRETURN_UNDEF;
561         }
562     }
563 }
564
565 XS(XS_Cygwin_is_binmount)
566 {
567     dXSARGS;
568     char *pathname;
569
570     if (items != 1)
571         Perl_croak(aTHX_ "Usage: Cygwin::is_binmount(pathname)");
572
573     pathname = SvPV_nolen(ST(0));
574
575     ST(0) = boolSV(cygwin_internal(CW_GET_BINMODE, pathname));
576     XSRETURN(1);
577 }
578
579 XS(XS_Cygwin_sync_winenv){ cygwin_internal(CW_SYNC_WINENV); }
580
581 void
582 init_os_extras(void)
583 {
584     dTHX;
585     char const *file = __FILE__;
586     void *handle;
587
588     newXS("Cwd::cwd", Cygwin_cwd, file);
589     newXSproto("Cygwin::winpid_to_pid", XS_Cygwin_winpid_to_pid, file, "$");
590     newXSproto("Cygwin::pid_to_winpid", XS_Cygwin_pid_to_winpid, file, "$");
591     newXSproto("Cygwin::win_to_posix_path", XS_Cygwin_win_to_posix_path, file, "$;$");
592     newXSproto("Cygwin::posix_to_win_path", XS_Cygwin_posix_to_win_path, file, "$;$");
593     newXSproto("Cygwin::mount_table", XS_Cygwin_mount_table, file, "");
594     newXSproto("Cygwin::mount_flags", XS_Cygwin_mount_flags, file, "$");
595     newXSproto("Cygwin::is_binmount", XS_Cygwin_is_binmount, file, "$");
596     newXS("Cygwin::sync_winenv", XS_Cygwin_sync_winenv, file);
597
598     /* Initialize Win32CORE if it has been statically linked. */
599     handle = dlopen(NULL, RTLD_LAZY);
600     if (handle) {
601         void (*pfn_init)(pTHX);
602         pfn_init = (void (*)(pTHX))dlsym(handle, "init_Win32CORE");
603         if (pfn_init)
604             pfn_init(aTHX);
605         dlclose(handle);
606     }
607 }