home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / perl560.zip / os2 / os2.c < prev    next >
C/C++ Source or Header  |  2000-03-11  |  53KB  |  2,260 lines

  1. #define INCL_DOS
  2. #define INCL_NOPM
  3. #define INCL_DOSFILEMGR
  4. #define INCL_DOSMEMMGR
  5. #define INCL_DOSERRORS
  6. /* These 3 are needed for compile if os2.h includes os2tk.h, not os2emx.h */
  7. #define INCL_DOSPROCESS
  8. #define SPU_DISABLESUPPRESSION          0
  9. #define SPU_ENABLESUPPRESSION           1
  10. #include <os2.h>
  11.  
  12. #include <sys/uflags.h>
  13.  
  14. /*
  15.  * Various Unix compatibility functions for OS/2
  16.  */
  17.  
  18. #include <stdio.h>
  19. #include <errno.h>
  20. #include <limits.h>
  21. #include <process.h>
  22. #include <fcntl.h>
  23.  
  24. #include "EXTERN.h"
  25. #include "perl.h"
  26.  
  27. #ifdef USE_THREADS
  28.  
  29. typedef void (*emx_startroutine)(void *);
  30. typedef void* (*pthreads_startroutine)(void *);
  31.  
  32. enum pthreads_state {
  33.     pthreads_st_none = 0, 
  34.     pthreads_st_run,
  35.     pthreads_st_exited, 
  36.     pthreads_st_detached, 
  37.     pthreads_st_waited,
  38. };
  39. const char *pthreads_states[] = {
  40.     "uninit",
  41.     "running",
  42.     "exited",
  43.     "detached",
  44.     "waited for",
  45. };
  46.  
  47. typedef struct {
  48.     void *status;
  49.     perl_cond cond;
  50.     enum pthreads_state state;
  51. } thread_join_t;
  52.  
  53. thread_join_t *thread_join_data;
  54. int thread_join_count;
  55. perl_mutex start_thread_mutex;
  56.  
  57. int
  58. pthread_join(perl_os_thread tid, void **status)
  59. {
  60.     MUTEX_LOCK(&start_thread_mutex);
  61.     switch (thread_join_data[tid].state) {
  62.     case pthreads_st_exited:
  63.     thread_join_data[tid].state = pthreads_st_none;    /* Ready to reuse */
  64.     MUTEX_UNLOCK(&start_thread_mutex);
  65.     *status = thread_join_data[tid].status;
  66.     break;
  67.     case pthreads_st_waited:
  68.     MUTEX_UNLOCK(&start_thread_mutex);
  69.     croak("join with a thread with a waiter");
  70.     break;
  71.     case pthreads_st_run:
  72.     thread_join_data[tid].state = pthreads_st_waited;
  73.     COND_INIT(&thread_join_data[tid].cond);
  74.     MUTEX_UNLOCK(&start_thread_mutex);
  75.     COND_WAIT(&thread_join_data[tid].cond, NULL);    
  76.     COND_DESTROY(&thread_join_data[tid].cond);
  77.     thread_join_data[tid].state = pthreads_st_none;    /* Ready to reuse */
  78.     *status = thread_join_data[tid].status;
  79.     break;
  80.     default:
  81.     MUTEX_UNLOCK(&start_thread_mutex);
  82.     croak("join: unknown thread state: '%s'", 
  83.           pthreads_states[thread_join_data[tid].state]);
  84.     break;
  85.     }
  86.     return 0;
  87. }
  88.  
  89. void
  90. pthread_startit(void *arg)
  91. {
  92.     /* Thread is already started, we need to transfer control only */
  93.     pthreads_startroutine start_routine = *((pthreads_startroutine*)arg);
  94.     int tid = pthread_self();
  95.     void *retval;
  96.     
  97.     arg = ((void**)arg)[1];
  98.     if (tid >= thread_join_count) {
  99.     int oc = thread_join_count;
  100.     
  101.     thread_join_count = tid + 5 + tid/5;
  102.     if (thread_join_data) {
  103.         Renew(thread_join_data, thread_join_count, thread_join_t);
  104.         Zero(thread_join_data + oc, thread_join_count - oc, thread_join_t);
  105.     } else {
  106.         Newz(1323, thread_join_data, thread_join_count, thread_join_t);
  107.     }
  108.     }
  109.     if (thread_join_data[tid].state != pthreads_st_none)
  110.     croak("attempt to reuse thread id %i", tid);
  111.     thread_join_data[tid].state = pthreads_st_run;
  112.     /* Now that we copied/updated the guys, we may release the caller... */
  113.     MUTEX_UNLOCK(&start_thread_mutex);
  114.     thread_join_data[tid].status = (*start_routine)(arg);
  115.     switch (thread_join_data[tid].state) {
  116.     case pthreads_st_waited:
  117.     COND_SIGNAL(&thread_join_data[tid].cond);    
  118.     break;
  119.     default:
  120.     thread_join_data[tid].state = pthreads_st_exited;
  121.     break;
  122.     }
  123. }
  124.  
  125. int
  126. pthread_create(perl_os_thread *tid, const pthread_attr_t *attr, 
  127.            void *(*start_routine)(void*), void *arg)
  128. {
  129.     void *args[2];
  130.  
  131.     args[0] = (void*)start_routine;
  132.     args[1] = arg;
  133.  
  134.     MUTEX_LOCK(&start_thread_mutex);
  135.     *tid = _beginthread(pthread_startit, /*stack*/ NULL, 
  136.             /*stacksize*/ 10*1024*1024, (void*)args);
  137.     MUTEX_LOCK(&start_thread_mutex);
  138.     MUTEX_UNLOCK(&start_thread_mutex);
  139.     return *tid ? 0 : EINVAL;
  140. }
  141.  
  142. int 
  143. pthread_detach(perl_os_thread tid)
  144. {
  145.     MUTEX_LOCK(&start_thread_mutex);
  146.     switch (thread_join_data[tid].state) {
  147.     case pthreads_st_waited:
  148.     MUTEX_UNLOCK(&start_thread_mutex);
  149.     croak("detach on a thread with a waiter");
  150.     break;
  151.     case pthreads_st_run:
  152.     thread_join_data[tid].state = pthreads_st_detached;
  153.     MUTEX_UNLOCK(&start_thread_mutex);
  154.     break;
  155.     default:
  156.     MUTEX_UNLOCK(&start_thread_mutex);
  157.     croak("detach: unknown thread state: '%s'", 
  158.           pthreads_states[thread_join_data[tid].state]);
  159.     break;
  160.     }
  161.     return 0;
  162. }
  163.  
  164. /* This is a very bastardized version: */
  165. int
  166. os2_cond_wait(perl_cond *c, perl_mutex *m)
  167. {                        
  168.     int rc;
  169.     STRLEN n_a;
  170.     if ((rc = DosResetEventSem(*c,&n_a)) && (rc != ERROR_ALREADY_RESET))
  171.     croak("panic: COND_WAIT-reset: rc=%i", rc);        
  172.     if (m) MUTEX_UNLOCK(m);                    
  173.     if (CheckOSError(DosWaitEventSem(*c,SEM_INDEFINITE_WAIT))
  174.     && (rc != ERROR_INTERRUPT))
  175.     croak("panic: COND_WAIT: rc=%i", rc);        
  176.     if (rc == ERROR_INTERRUPT)
  177.     errno = EINTR;
  178.     if (m) MUTEX_LOCK(m);                    
  179. #endif 
  180.  
  181. /*****************************************************************************/
  182. /* 2.1 would not resolve symbols on demand, and has no ExtLIBPATH. */
  183. static PFN ExtFCN[2];            /* Labeled by ord below. */
  184. static USHORT loadOrd[2] = { 874, 873 }; /* Query=874, Set=873. */
  185. #define ORD_QUERY_ELP    0
  186. #define ORD_SET_ELP    1
  187. struct PMWIN_entries_t PMWIN_entries;
  188.  
  189. APIRET
  190. loadByOrd(char *modname, ULONG ord)
  191. {
  192.     if (ExtFCN[ord] == NULL) {
  193.     static HMODULE hdosc = 0;
  194.     BYTE buf[20];
  195.     PFN fcn;
  196.     APIRET rc;
  197.  
  198.     if ((!hdosc && CheckOSError(DosLoadModule(buf, sizeof buf, 
  199.                           modname, &hdosc)))
  200.         || CheckOSError(DosQueryProcAddr(hdosc, loadOrd[ord], NULL, &fcn)))
  201.         croak("This version of OS/2 does not support %s.%i", 
  202.           modname, loadOrd[ord]);
  203.     ExtFCN[ord] = fcn;
  204.     } 
  205.     if ((long)ExtFCN[ord] == -1) 
  206.     croak("panic queryaddr");
  207. }
  208.  
  209. void 
  210. init_PMWIN_entries(void)
  211. {
  212.     static HMODULE hpmwin = 0;
  213.     static const int ords[] = {
  214.     763,                /* Initialize */
  215.     716,                /* CreateMsgQueue */
  216.     726,                /* DestroyMsgQueue */
  217.     918,                /* PeekMsg */
  218.     915,                /* GetMsg */
  219.     912,                /* DispatchMsg */
  220.     };
  221.     BYTE buf[20];
  222.     int i = 0;
  223.     unsigned long rc;
  224.  
  225.     if (hpmwin)
  226.     return;
  227.  
  228.     if (CheckOSError(DosLoadModule(buf, sizeof buf, "pmwin", &hpmwin)))
  229.     croak("This version of OS/2 does not support pmwin: error in %s", buf);
  230.     while (i <= 5) {
  231.     if (CheckOSError(DosQueryProcAddr(hpmwin, ords[i], NULL, 
  232.                       ((PFN*)&PMWIN_entries)+i)))
  233.         croak("This version of OS/2 does not support pmwin.%d", ords[i]);
  234.     i++;
  235.     }
  236. }
  237.  
  238.  
  239. /* priorities */
  240. static signed char priors[] = {0, 1, 3, 2}; /* Last two interchanged,
  241.                            self inverse. */
  242. #define QSS_INI_BUFFER 1024
  243.  
  244. PQTOPLEVEL
  245. get_sysinfo(ULONG pid, ULONG flags)
  246. {
  247.     char *pbuffer;
  248.     ULONG rc, buf_len = QSS_INI_BUFFER;
  249.  
  250.     New(1322, pbuffer, buf_len, char);
  251.     /* QSS_PROCESS | QSS_MODULE | QSS_SEMAPHORES | QSS_SHARED */
  252.     rc = QuerySysState(flags, pid, pbuffer, buf_len);
  253.     while (rc == ERROR_BUFFER_OVERFLOW) {
  254.     Renew(pbuffer, buf_len *= 2, char);
  255.     rc = QuerySysState(flags, pid, pbuffer, buf_len);
  256.     }
  257.     if (rc) {
  258.     FillOSError(rc);
  259.     Safefree(pbuffer);
  260.     return 0;
  261.     }
  262.     return (PQTOPLEVEL)pbuffer;
  263. }
  264.  
  265. #define PRIO_ERR 0x1111
  266.  
  267. static ULONG
  268. sys_prio(pid)
  269. {
  270.   ULONG prio;
  271.   PQTOPLEVEL psi;
  272.  
  273.   psi = get_sysinfo(pid, QSS_PROCESS);
  274.   if (!psi) {
  275.       return PRIO_ERR;
  276.   }
  277.   if (pid != psi->procdata->pid) {
  278.       Safefree(psi);
  279.       croak("panic: wrong pid in sysinfo");
  280.   }
  281.   prio = psi->procdata->threads->priority;
  282.   Safefree(psi);
  283.   return prio;
  284. }
  285.  
  286. int 
  287. setpriority(int which, int pid, int val)
  288. {
  289.   ULONG rc, prio;
  290.   PQTOPLEVEL psi;
  291.  
  292.   prio = sys_prio(pid);
  293.  
  294.   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
  295.   if (priors[(32 - val) >> 5] + 1 == (prio >> 8)) {
  296.       /* Do not change class. */
  297.       return CheckOSError(DosSetPriority((pid < 0) 
  298.                      ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
  299.                      0, 
  300.                      (32 - val) % 32 - (prio & 0xFF), 
  301.                      abs(pid)))
  302.       ? -1 : 0;
  303.   } else /* if ((32 - val) % 32 == (prio & 0xFF)) */ {
  304.       /* Documentation claims one can change both class and basevalue,
  305.        * but I find it wrong. */
  306.       /* Change class, but since delta == 0 denotes absolute 0, correct. */
  307.       if (CheckOSError(DosSetPriority((pid < 0) 
  308.                       ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
  309.                       priors[(32 - val) >> 5] + 1, 
  310.                       0, 
  311.                       abs(pid)))) 
  312.       return -1;
  313.       if ( ((32 - val) % 32) == 0 ) return 0;
  314.       return CheckOSError(DosSetPriority((pid < 0) 
  315.                      ? PRTYS_PROCESSTREE : PRTYS_PROCESS,
  316.                      0, 
  317.                      (32 - val) % 32, 
  318.                      abs(pid)))
  319.       ? -1 : 0;
  320.   } 
  321. /*   else return CheckOSError(DosSetPriority((pid < 0)  */
  322. /*                       ? PRTYS_PROCESSTREE : PRTYS_PROCESS, */
  323. /*                       priors[(32 - val) >> 5] + 1,  */
  324. /*                       (32 - val) % 32 - (prio & 0xFF),  */
  325. /*                       abs(pid))) */
  326. /*       ? -1 : 0; */
  327. }
  328.  
  329. int 
  330. getpriority(int which /* ignored */, int pid)
  331. {
  332.   TIB *tib;
  333.   PIB *pib;
  334.   ULONG rc, ret;
  335.  
  336.   if (!(_emx_env & 0x200)) return 0; /* Nop if not OS/2. */
  337.   /* DosGetInfoBlocks has old priority! */
  338. /*   if (CheckOSError(DosGetInfoBlocks(&tib, &pib))) return -1; */
  339. /*   if (pid != pib->pib_ulpid) { */
  340.   ret = sys_prio(pid);
  341.   if (ret == PRIO_ERR) {
  342.       return -1;
  343.   }
  344. /*   } else */
  345. /*       ret = tib->tib_ptib2->tib2_ulpri; */
  346.   return (1 - priors[((ret >> 8) - 1)])*32 - (ret & 0xFF);
  347. }
  348.  
  349. /*****************************************************************************/
  350. /* spawn */
  351.  
  352. /* There is no big sense to make it thread-specific, since signals 
  353.    are delivered to thread 1 only.  XXXX Maybe make it into an array? */
  354. static int spawn_pid;
  355. static int spawn_killed;
  356.  
  357. static Signal_t
  358. spawn_sighandler(int sig)
  359. {
  360.     /* Some programs do not arrange for the keyboard signals to be
  361.        delivered to them.  We need to deliver the signal manually. */
  362.     /* We may get a signal only if 
  363.        a) kid does not receive keyboard signal: deliver it;
  364.        b) kid already died, and we get a signal.  We may only hope
  365.           that the pid number was not reused.
  366.      */
  367.     
  368.     if (spawn_killed) 
  369.     sig = SIGKILL;            /* Try harder. */
  370.     kill(spawn_pid, sig);
  371.     spawn_killed = 1;
  372. }
  373.  
  374. static int
  375. result(int flag, int pid)
  376. {
  377.     int r, status;
  378.     Signal_t (*ihand)();     /* place to save signal during system() */
  379.     Signal_t (*qhand)();     /* place to save signal during system() */
  380. #ifndef __EMX__
  381.     RESULTCODES res;
  382.     int rpid;
  383. #endif
  384.  
  385.     if (pid < 0 || flag != 0)
  386.         return pid;
  387.  
  388. #ifdef __EMX__
  389.     spawn_pid = pid;
  390.     spawn_killed = 0;
  391.     ihand = rsignal(SIGINT, &spawn_sighandler);
  392.     qhand = rsignal(SIGQUIT, &spawn_sighandler);
  393.     do {
  394.         r = wait4pid(pid, &status, 0);
  395.     } while (r == -1 && errno == EINTR);
  396.     rsignal(SIGINT, ihand);
  397.     rsignal(SIGQUIT, qhand);
  398.  
  399.     PL_statusvalue = (U16)status;
  400.     if (r < 0)
  401.         return -1;
  402.     return status & 0xFFFF;
  403. #else
  404.     ihand = rsignal(SIGINT, SIG_IGN);
  405.     r = DosWaitChild(DCWA_PROCESS, DCWW_WAIT, &res, &rpid, pid);
  406.     rsignal(SIGINT, ihand);
  407.     PL_statusvalue = res.codeResult << 8 | res.codeTerminate;
  408.     if (r)
  409.         return -1;
  410.     return PL_statusvalue;
  411. #endif
  412. }
  413.  
  414. #define EXECF_SPAWN 0
  415. #define EXECF_EXEC 1
  416. #define EXECF_TRUEEXEC 2
  417. #define EXECF_SPAWN_NOWAIT 3
  418. #define EXECF_SPAWN_BYFLAG 4
  419.  
  420. /* const char* const ptypes[] = { "FS", "DOS", "VIO", "PM", "DETACH" }; */
  421.  
  422. static int
  423. my_type()
  424. {
  425.     int rc;
  426.     TIB *tib;
  427.     PIB *pib;
  428.     
  429.     if (!(_emx_env & 0x200)) return 1; /* not OS/2. */
  430.     if (CheckOSError(DosGetInfoBlocks(&tib, &pib))) 
  431.     return -1; 
  432.     
  433.     return (pib->pib_ultype);
  434. }
  435.  
  436. static ULONG
  437. file_type(char *path)
  438. {
  439.     int rc;
  440.     ULONG apptype;
  441.     
  442.     if (!(_emx_env & 0x200)) 
  443.     croak("file_type not implemented on DOS"); /* not OS/2. */
  444.     if (CheckOSError(DosQueryAppType(path, &apptype))) {
  445.     switch (rc) {
  446.     case ERROR_FILE_NOT_FOUND:
  447.     case ERROR_PATH_NOT_FOUND:
  448.         return -1;
  449.     case ERROR_ACCESS_DENIED:    /* Directory with this name found? */
  450.         return -3;
  451.     default:            /* Found, but not an
  452.                        executable, or some other
  453.                        read error. */
  454.         return -2;
  455.     }
  456.     }    
  457.     return apptype;
  458. }
  459.  
  460. static ULONG os2_mytype;
  461.  
  462. /* Spawn/exec a program, revert to shell if needed. */
  463. /* global PL_Argv[] contains arguments. */
  464.  
  465. int
  466. do_spawn_ve(really, flag, execf, inicmd, addflag)
  467. SV *really;
  468. U32 flag;
  469. U32 execf;
  470. char *inicmd;
  471. U32 addflag;
  472. {
  473.     dTHR;
  474.     int trueflag = flag;
  475.     int rc, pass = 1;
  476.     char *tmps;
  477.     char buf[256], *s = 0, scrbuf[280];
  478.     char *args[4];
  479.     static char * fargs[4] 
  480.         = { "/bin/sh", "-c", "\"$@\"", "spawn-via-shell", };
  481.     char **argsp = fargs;
  482.     char nargs = 4;
  483.     int force_shell;
  484.      int new_stderr = -1, nostderr = 0, fl_stderr;
  485.     STRLEN n_a;
  486.     
  487.     if (flag == P_WAIT)
  488.         flag = P_NOWAIT;
  489.  
  490.       retry:
  491.     if (strEQ(PL_Argv[0],"/bin/sh")) 
  492.         PL_Argv[0] = PL_sh_path;
  493.  
  494.     if (PL_Argv[0][0] != '/' && PL_Argv[0][0] != '\\'
  495.         && !(PL_Argv[0][0] && PL_Argv[0][1] == ':' 
  496.          && (PL_Argv[0][2] == '/' || PL_Argv[0][2] != '\\'))
  497.         ) /* will spawnvp use PATH? */
  498.         TAINT_ENV();    /* testing IFS here is overkill, probably */
  499.     /* We should check PERL_SH* and PERLLIB_* as well? */
  500.     if (!really || !*(tmps = SvPV(really, n_a)))
  501.         tmps = PL_Argv[0];
  502.  
  503.       reread:
  504.     force_shell = 0;
  505.     if (_emx_env & 0x200) { /* OS/2. */ 
  506.         int type = file_type(tmps);
  507.       type_again:
  508.         if (type == -1) {        /* Not found */
  509.         errno = ENOENT;
  510.         rc = -1;
  511.         goto do_script;
  512.         }
  513.         else if (type == -2) {        /* Not an EXE */
  514.         errno = ENOEXEC;
  515.         rc = -1;
  516.         goto do_script;
  517.         }
  518.         else if (type == -3) {        /* Is a directory? */
  519.         /* Special-case this */
  520.         char tbuf[512];
  521.         int l = strlen(tmps);
  522.  
  523.         if (l + 5 <= sizeof tbuf) {
  524.             strcpy(tbuf, tmps);
  525.             strcpy(tbuf + l, ".exe");
  526.             type = file_type(tbuf);
  527.             if (type >= -3)
  528.             goto type_again;
  529.         }
  530.         
  531.         errno = ENOEXEC;
  532.         rc = -1;
  533.         goto do_script;
  534.         }
  535.         switch (type & 7) {
  536.         /* Ignore WINDOWCOMPAT and FAPI, start them the same type we are. */
  537.         case FAPPTYP_WINDOWAPI: 
  538.         {
  539.         if (os2_mytype != 3) {    /* not PM */
  540.             if (flag == P_NOWAIT)
  541.             flag = P_PM;
  542.             else if ((flag & 7) != P_PM && (flag & 7) != P_SESSION)
  543.             warn("Starting PM process with flag=%d, mytype=%d",
  544.                  flag, os2_mytype);
  545.         }
  546.         }
  547.         break;
  548.         case FAPPTYP_NOTWINDOWCOMPAT: 
  549.         {
  550.         if (os2_mytype != 0) {    /* not full screen */
  551.             if (flag == P_NOWAIT)
  552.             flag = P_SESSION;
  553.             else if ((flag & 7) != P_SESSION)
  554.             warn("Starting Full Screen process with flag=%d, mytype=%d",
  555.                  flag, os2_mytype);
  556.         }
  557.         }
  558.         break;
  559.         case FAPPTYP_NOTSPEC: 
  560.         /* Let the shell handle this... */
  561.         force_shell = 1;
  562.         goto doshell_args;
  563.         break;
  564.         }
  565.     }
  566.  
  567.     if (addflag) {
  568.         addflag = 0;
  569.         new_stderr = dup(2);        /* Preserve stderr */
  570.         if (new_stderr == -1) {
  571.         if (errno == EBADF)
  572.             nostderr = 1;
  573.         else {
  574.             rc = -1;
  575.             goto finish;
  576.         }
  577.         } else
  578.         fl_stderr = fcntl(2, F_GETFD);
  579.         rc = dup2(1,2);
  580.         if (rc == -1)
  581.         goto finish;
  582.         fcntl(new_stderr, F_SETFD, FD_CLOEXEC);
  583.     }
  584.  
  585. #if 0
  586.     rc = result(trueflag, spawnvp(flag,tmps,PL_Argv));
  587. #else
  588.     if (execf == EXECF_TRUEEXEC)
  589.         rc = execvp(tmps,PL_Argv);
  590.     else if (execf == EXECF_EXEC)
  591.         rc = spawnvp(trueflag | P_OVERLAY,tmps,PL_Argv);
  592.     else if (execf == EXECF_SPAWN_NOWAIT)
  593.         rc = spawnvp(flag,tmps,PL_Argv);
  594.         else                /* EXECF_SPAWN, EXECF_SPAWN_BYFLAG */
  595.         rc = result(trueflag, 
  596.             spawnvp(flag,tmps,PL_Argv));
  597. #endif 
  598.     if (rc < 0 && pass == 1
  599.         && (tmps == PL_Argv[0])) { /* Cannot transfer `really' via shell. */
  600.           do_script:
  601.         {
  602.         int err = errno;
  603.  
  604.         if (err == ENOENT || err == ENOEXEC) {
  605.         /* No such file, or is a script. */
  606.         /* Try adding script extensions to the file name, and
  607.            search on PATH. */
  608.         char *scr = find_script(PL_Argv[0], TRUE, NULL, 0);
  609.  
  610.         if (scr) {
  611.             FILE *file;
  612.             char *s = 0, *s1;
  613.             int l;
  614.  
  615.                     l = strlen(scr);
  616.         
  617.                     if (l >= sizeof scrbuf) {
  618.                        Safefree(scr);
  619.                      longbuf:
  620.                        warn("Size of scriptname too big: %d", l);
  621.                rc = -1;
  622.                goto finish;
  623.                     }
  624.                     strcpy(scrbuf, scr);
  625.                     Safefree(scr);
  626.                     scr = scrbuf;
  627.  
  628.             file = fopen(scr, "r");
  629.             PL_Argv[0] = scr;
  630.             if (!file)
  631.             goto panic_file;
  632.             if (!fgets(buf, sizeof buf, file)) { /* Empty... */
  633.  
  634.             buf[0] = 0;
  635.             fclose(file);
  636.             /* Special case: maybe from -Zexe build, so
  637.                there is an executable around (contrary to
  638.                documentation, DosQueryAppType sometimes (?)
  639.                does not append ".exe", so we could have
  640.                reached this place). */
  641.             if (l + 5 < sizeof scrbuf) {
  642.                 strcpy(scrbuf + l, ".exe");
  643.                 if (PerlLIO_stat(scrbuf,&PL_statbuf) >= 0
  644.                 && !S_ISDIR(PL_statbuf.st_mode)) {
  645.                 /* Found */
  646.                 tmps = scr;
  647.                 pass++;
  648.                 goto reread;
  649.                 } else
  650.                 scrbuf[l] = 0;
  651.             } else
  652.                 goto longbuf;
  653.             }
  654.             if (fclose(file) != 0) { /* Failure */
  655.               panic_file:
  656.             warn("Error reading \"%s\": %s", 
  657.                  scr, Strerror(errno));
  658.             buf[0] = 0;    /* Not #! */
  659.             goto doshell_args;
  660.             }
  661.             if (buf[0] == '#') {
  662.             if (buf[1] == '!')
  663.                 s = buf + 2;
  664.             } else if (buf[0] == 'e') {
  665.             if (strnEQ(buf, "extproc", 7) 
  666.                 && isSPACE(buf[7]))
  667.                 s = buf + 8;
  668.             } else if (buf[0] == 'E') {
  669.             if (strnEQ(buf, "EXTPROC", 7)
  670.                 && isSPACE(buf[7]))
  671.                 s = buf + 8;
  672.             }
  673.             if (!s) {
  674.             buf[0] = 0;    /* Not #! */
  675.             goto doshell_args;
  676.             }
  677.             
  678.             s1 = s;
  679.             nargs = 0;
  680.             argsp = args;
  681.             while (1) {
  682.             /* Do better than pdksh: allow a few args,
  683.                strip trailing whitespace.  */
  684.             while (isSPACE(*s))
  685.                 s++;
  686.             if (*s == 0) 
  687.                 break;
  688.             if (nargs == 4) {
  689.                 nargs = -1;
  690.                 break;
  691.             }
  692.             args[nargs++] = s;
  693.             while (*s && !isSPACE(*s))
  694.                 s++;
  695.             if (*s == 0) 
  696.                 break;
  697.             *s++ = 0;
  698.             }
  699.             if (nargs == -1) {
  700.             warn("Too many args on %.*s line of \"%s\"",
  701.                  s1 - buf, buf, scr);
  702.             nargs = 4;
  703.             argsp = fargs;
  704.             }
  705.           doshell_args:
  706.             {
  707.             char **a = PL_Argv;
  708.             char *exec_args[2];
  709.  
  710.             if (force_shell 
  711.                 || (!buf[0] && file)) { /* File without magic */
  712.                 /* In fact we tried all what pdksh would
  713.                    try.  There is no point in calling
  714.                    pdksh, we may just emulate its logic. */
  715.                 char *shell = getenv("EXECSHELL");
  716.                 char *shell_opt = NULL;
  717.  
  718.                 if (!shell) {
  719.                 char *s;
  720.  
  721.                 shell_opt = "/c";
  722.                 shell = getenv("OS2_SHELL");
  723.                 if (inicmd) { /* No spaces at start! */
  724.                     s = inicmd;
  725.                     while (*s && !isSPACE(*s)) {
  726.                     if (*s++ = '/') {
  727.                         inicmd = NULL; /* Cannot use */
  728.                         break;
  729.                     }
  730.                     }
  731.                 }
  732.                 if (!inicmd) {
  733.                     s = PL_Argv[0];
  734.                     while (*s) { 
  735.                     /* Dosish shells will choke on slashes
  736.                        in paths, fortunately, this is
  737.                        important for zeroth arg only. */
  738.                     if (*s == '/') 
  739.                         *s = '\\';
  740.                     s++;
  741.                     }
  742.                 }
  743.                 }
  744.                 /* If EXECSHELL is set, we do not set */
  745.                 
  746.                 if (!shell)
  747.                 shell = ((_emx_env & 0x200)
  748.                      ? "c:/os2/cmd.exe"
  749.                      : "c:/command.com");
  750.                 nargs = shell_opt ? 2 : 1;    /* shell file args */
  751.                 exec_args[0] = shell;
  752.                 exec_args[1] = shell_opt;
  753.                 argsp = exec_args;
  754.                 if (nargs == 2 && inicmd) {
  755.                 /* Use the original cmd line */
  756.                 /* XXXX This is good only until we refuse
  757.                         quoted arguments... */
  758.                 PL_Argv[0] = inicmd;
  759.                 PL_Argv[1] = Nullch;
  760.                 }
  761.             } else if (!buf[0] && inicmd) { /* No file */
  762.                 /* Start with the original cmdline. */
  763.                 /* XXXX This is good only until we refuse
  764.                         quoted arguments... */
  765.  
  766.                 PL_Argv[0] = inicmd;
  767.                 PL_Argv[1] = Nullch;
  768.                 nargs = 2;    /* shell -c */
  769.             } 
  770.  
  771.             while (a[1])        /* Get to the end */
  772.                 a++;
  773.             a++;            /* Copy finil NULL too */
  774.             while (a >= PL_Argv) {
  775.                 *(a + nargs) = *a;    /* PL_Argv was preallocated to be
  776.                            long enough. */
  777.                 a--;
  778.             }
  779.             while (--nargs >= 0)
  780.                 PL_Argv[nargs] = argsp[nargs];
  781.             /* Enable pathless exec if #! (as pdksh). */
  782.             pass = (buf[0] == '#' ? 2 : 3);
  783.             goto retry;
  784.             }
  785.         }
  786.         /* Not found: restore errno */
  787.         errno = err;
  788.         }
  789.       }
  790.     } else if (rc < 0 && pass == 2 && errno == ENOENT) { /* File not found */
  791.         char *no_dir = strrchr(PL_Argv[0], '/');
  792.  
  793.         /* Do as pdksh port does: if not found with /, try without
  794.            path. */
  795.         if (no_dir) {
  796.         PL_Argv[0] = no_dir + 1;
  797.         pass++;
  798.         goto retry;
  799.         }
  800.     }
  801.     if (rc < 0 && ckWARN(WARN_EXEC))
  802.         Perl_warner(aTHX_ WARN_EXEC, "Can't %s \"%s\": %s\n", 
  803.          ((execf != EXECF_EXEC && execf != EXECF_TRUEEXEC) 
  804.           ? "spawn" : "exec"),
  805.          PL_Argv[0], Strerror(errno));
  806.     if (rc < 0 && (execf != EXECF_SPAWN_NOWAIT) 
  807.         && ((trueflag & 0xFF) == P_WAIT)) 
  808.         rc = -1;
  809.  
  810.   finish:
  811.     if (new_stderr != -1) {    /* How can we use error codes? */
  812.     dup2(new_stderr, 2);
  813.     close(new_stderr);
  814.     fcntl(2, F_SETFD, fl_stderr);
  815.     } else if (nostderr)
  816.        close(2);
  817.     return rc;
  818. }
  819.  
  820. /* Try converting 1-arg form to (usually shell-less) multi-arg form. */
  821. int
  822. do_spawn3(char *cmd, int execf, int flag)
  823. {
  824.     register char **a;
  825.     register char *s;
  826.     char flags[10];
  827.     char *shell, *copt, *news = NULL;
  828.     int rc, err, seenspace = 0, mergestderr = 0;
  829.     char fullcmd[MAXNAMLEN + 1];
  830.  
  831. #ifdef TRYSHELL
  832.     if ((shell = getenv("EMXSHELL")) != NULL)
  833.         copt = "-c";
  834.     else if ((shell = getenv("SHELL")) != NULL)
  835.         copt = "-c";
  836.     else if ((shell = getenv("COMSPEC")) != NULL)
  837.         copt = "/C";
  838.     else
  839.         shell = "cmd.exe";
  840. #else
  841.     /* Consensus on perl5-porters is that it is _very_ important to
  842.        have a shell which will not change between computers with the
  843.        same architecture, to avoid "action on a distance". 
  844.        And to have simple build, this shell should be sh. */
  845.     shell = PL_sh_path;
  846.     copt = "-c";
  847. #endif 
  848.  
  849.     while (*cmd && isSPACE(*cmd))
  850.     cmd++;
  851.  
  852.     if (strnEQ(cmd,"/bin/sh",7) && isSPACE(cmd[7])) {
  853.     STRLEN l = strlen(PL_sh_path);
  854.     
  855.     New(1302, news, strlen(cmd) - 7 + l + 1, char);
  856.     strcpy(news, PL_sh_path);
  857.     strcpy(news + l, cmd + 7);
  858.     cmd = news;
  859.     }
  860.  
  861.     /* save an extra exec if possible */
  862.     /* see if there are shell metacharacters in it */
  863.  
  864.     if (*cmd == '.' && isSPACE(cmd[1]))
  865.     goto doshell;
  866.  
  867.     if (strnEQ(cmd,"exec",4) && isSPACE(cmd[4]))
  868.     goto doshell;
  869.  
  870.     for (s = cmd; *s && isALPHA(*s); s++) ;    /* catch VAR=val gizmo */
  871.     if (*s == '=')
  872.     goto doshell;
  873.  
  874.     for (s = cmd; *s; s++) {
  875.     if (*s != ' ' && !isALPHA(*s) && strchr("$&*(){}[]'\";\\|?<>~`\n",*s)) {
  876.         if (*s == '\n' && s[1] == '\0') {
  877.         *s = '\0';
  878.         break;
  879.         } else if (*s == '\\' && !seenspace) {
  880.         continue;        /* Allow backslashes in names */
  881.         } else if (*s == '>' && s >= cmd + 3
  882.             && s[-1] == '2' && s[1] == '&' && s[2] == '1'
  883.             && isSPACE(s[-2]) ) {
  884.         char *t = s + 3;
  885.  
  886.         while (*t && isSPACE(*t))
  887.             t++;
  888.         if (!*t) {
  889.             s[-2] = '\0';
  890.             mergestderr = 1;
  891.             break;        /* Allow 2>&1 as the last thing */
  892.         }
  893.         }
  894.         /* We do not convert this to do_spawn_ve since shell
  895.            should be smart enough to start itself gloriously. */
  896.       doshell:
  897.         if (execf == EXECF_TRUEEXEC)
  898.                 rc = execl(shell,shell,copt,cmd,(char*)0);        
  899.         else if (execf == EXECF_EXEC)
  900.                 rc = spawnl(P_OVERLAY,shell,shell,copt,cmd,(char*)0);
  901.         else if (execf == EXECF_SPAWN_NOWAIT)
  902.                 rc = spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0);
  903.         else if (execf == EXECF_SPAWN_BYFLAG)
  904.                 rc = spawnl(flag,shell,shell,copt,cmd,(char*)0);
  905.         else {
  906.         /* In the ak code internal P_NOWAIT is P_WAIT ??? */
  907.         rc = result(P_WAIT,
  908.                 spawnl(P_NOWAIT,shell,shell,copt,cmd,(char*)0));
  909.         if (rc < 0 && ckWARN(WARN_EXEC))
  910.             Perl_warner(aTHX_ WARN_EXEC, "Can't %s \"%s\": %s", 
  911.              (execf == EXECF_SPAWN ? "spawn" : "exec"),
  912.              shell, Strerror(errno));
  913.         if (rc < 0)
  914.             rc = -1;
  915.         }
  916.         if (news)
  917.         Safefree(news);
  918.         return rc;
  919.     } else if (*s == ' ' || *s == '\t') {
  920.         seenspace = 1;
  921.     }
  922.     }
  923.  
  924.     /* cmd="a" may lead to "sh", "-c", "\"$@\"", "a", "a.cmd", NULL */
  925.     New(1303,PL_Argv, (s - cmd + 11) / 2, char*);
  926.     PL_Cmd = savepvn(cmd, s-cmd);
  927.     a = PL_Argv;
  928.     for (s = PL_Cmd; *s;) {
  929.     while (*s && isSPACE(*s)) s++;
  930.     if (*s)
  931.         *(a++) = s;
  932.     while (*s && !isSPACE(*s)) s++;
  933.     if (*s)
  934.         *s++ = '\0';
  935.     }
  936.     *a = Nullch;
  937.     if (PL_Argv[0])
  938.     rc = do_spawn_ve(NULL, flag, execf, cmd, mergestderr);
  939.     else
  940.         rc = -1;
  941.     if (news)
  942.     Safefree(news);
  943.     do_execfree();
  944.     return rc;
  945. }
  946.  
  947. /* Array spawn.  */
  948. int
  949. do_aspawn(really,mark,sp)
  950. SV *really;
  951. register SV **mark;
  952. register SV **sp;
  953. {
  954.     dTHR;
  955.     register char **a;
  956.     int rc;
  957.     int flag = P_WAIT, flag_set = 0;
  958.     STRLEN n_a;
  959.  
  960.     if (sp > mark) {
  961.     New(1301,PL_Argv, sp - mark + 3, char*);
  962.     a = PL_Argv;
  963.  
  964.     if (mark < sp && SvNIOKp(*(mark+1)) && !SvPOKp(*(mark+1))) {
  965.         ++mark;
  966.         flag = SvIVx(*mark);
  967.         flag_set = 1;
  968.  
  969.     }
  970.  
  971.     while (++mark <= sp) {
  972.         if (*mark)
  973.         *a++ = SvPVx(*mark, n_a);
  974.         else
  975.         *a++ = "";
  976.     }
  977.     *a = Nullch;
  978.  
  979.     if (flag_set && (a == PL_Argv + 1)) { /* One arg? */
  980.         rc = do_spawn3(a[-1], EXECF_SPAWN_BYFLAG, flag);
  981.     } else
  982.         rc = do_spawn_ve(really, flag, EXECF_SPAWN, NULL, 0);
  983.     } else
  984.         rc = -1;
  985.     do_execfree();
  986.     return rc;
  987. }
  988.  
  989. int
  990. do_spawn(cmd)
  991. char *cmd;
  992. {
  993.     return do_spawn3(cmd, EXECF_SPAWN, 0);
  994. }
  995.  
  996. int
  997. do_spawn_nowait(cmd)
  998. char *cmd;
  999. {
  1000.     return do_spawn3(cmd, EXECF_SPAWN_NOWAIT,0);
  1001. }
  1002.  
  1003. bool
  1004. do_exec(cmd)
  1005. char *cmd;
  1006. {
  1007.     do_spawn3(cmd, EXECF_EXEC, 0);
  1008.     return FALSE;
  1009. }
  1010.  
  1011. bool
  1012. os2exec(cmd)
  1013. char *cmd;
  1014. {
  1015.     return do_spawn3(cmd, EXECF_TRUEEXEC, 0);
  1016. }
  1017.  
  1018. PerlIO *
  1019. my_syspopen(cmd,mode)
  1020. char    *cmd;
  1021. char    *mode;
  1022. {
  1023. #ifndef USE_POPEN
  1024.  
  1025.     int p[2];
  1026.     register I32 this, that, newfd;
  1027.     register I32 pid, rc;
  1028.     PerlIO *res;
  1029.     SV *sv;
  1030.     int fh_fl;
  1031.     
  1032.     /* `this' is what we use in the parent, `that' in the child. */
  1033.     this = (*mode == 'w');
  1034.     that = !this;
  1035.     if (PL_tainting) {
  1036.     taint_env();
  1037.     taint_proper("Insecure %s%s", "EXEC");
  1038.     }
  1039.     if (pipe(p) < 0)
  1040.     return Nullfp;
  1041.     /* Now we need to spawn the child. */
  1042.     if (p[this] == (*mode == 'r')) {    /* if fh 0/1 was initially closed. */
  1043.     int new = dup(p[this]);
  1044.  
  1045.     if (new == -1)
  1046.         goto closepipes;
  1047.     close(p[this]);
  1048.     p[this] = new;
  1049.     }
  1050.     newfd = dup(*mode == 'r');        /* Preserve std* */
  1051.     if (newfd == -1) {        
  1052.     /* This cannot happen due to fh being bad after pipe(), since
  1053.        pipe() should have created fh 0 and 1 even if they were
  1054.        initially closed.  But we closed p[this] before.  */
  1055.     if (errno != EBADF) {
  1056.       closepipes:
  1057.         close(p[0]);
  1058.         close(p[1]);
  1059.         return Nullfp;
  1060.     }
  1061.     } else
  1062.     fh_fl = fcntl(*mode == 'r', F_GETFD);
  1063.     if (p[that] != (*mode == 'r')) {    /* if fh 0/1 was initially closed. */
  1064.     dup2(p[that], *mode == 'r');
  1065.     close(p[that]);
  1066.     }
  1067.     /* Where is `this' and newfd now? */
  1068.     fcntl(p[this], F_SETFD, FD_CLOEXEC);
  1069.     if (newfd != -1)
  1070.     fcntl(newfd, F_SETFD, FD_CLOEXEC);
  1071.     pid = do_spawn_nowait(cmd);
  1072.     if (newfd == -1)
  1073.     close(*mode == 'r');        /* It was closed initially */
  1074.     else if (newfd != (*mode == 'r')) {    /* Probably this check is not needed */
  1075.     dup2(newfd, *mode == 'r');    /* Return std* back. */
  1076.     close(newfd);
  1077.     fcntl(*mode == 'r', F_SETFD, fh_fl);
  1078.     } else
  1079.     fcntl(*mode == 'r', F_SETFD, fh_fl);
  1080.     if (p[that] == (*mode == 'r'))
  1081.     close(p[that]);
  1082.     if (pid == -1) {
  1083.     close(p[this]);
  1084.     return Nullfp;
  1085.     }
  1086.     if (p[that] < p[this]) {        /* Make fh as small as possible */
  1087.     dup2(p[this], p[that]);
  1088.     close(p[this]);
  1089.     p[this] = p[that];
  1090.     }
  1091.     sv = *av_fetch(PL_fdpid,p[this],TRUE);
  1092.     (void)SvUPGRADE(sv,SVt_IV);
  1093.     SvIVX(sv) = pid;
  1094.     PL_forkprocess = pid;
  1095.     return PerlIO_fdopen(p[this], mode);
  1096.  
  1097. #else  /* USE_POPEN */
  1098.  
  1099.     PerlIO *res;
  1100.     SV *sv;
  1101.  
  1102. #  ifdef TRYSHELL
  1103.     res = popen(cmd, mode);
  1104. #  else
  1105.     char *shell = getenv("EMXSHELL");
  1106.  
  1107.     my_setenv("EMXSHELL", PL_sh_path);
  1108.     res = popen(cmd, mode);
  1109.     my_setenv("EMXSHELL", shell);
  1110. #  endif 
  1111.     sv = *av_fetch(PL_fdpid, PerlIO_fileno(res), TRUE);
  1112.     (void)SvUPGRADE(sv,SVt_IV);
  1113.     SvIVX(sv) = -1;            /* A cooky. */
  1114.     return res;
  1115.  
  1116. #endif /* USE_POPEN */
  1117.  
  1118. }
  1119.  
  1120. /******************************************************************/
  1121.  
  1122. #ifndef HAS_FORK
  1123. int
  1124. fork(void)
  1125. {
  1126.     croak(PL_no_func, "Unsupported function fork");
  1127.     errno = EINVAL;
  1128.     return -1;
  1129. }
  1130. #endif
  1131.  
  1132. /*******************************************************************/
  1133. /* not implemented in EMX 0.9a */
  1134.  
  1135. void *    ctermid(x)    { return 0; }
  1136.  
  1137. #ifdef MYTTYNAME /* was not in emx0.9a */
  1138. void *    ttyname(x)    { return 0; }
  1139. #endif
  1140.  
  1141. /******************************************************************/
  1142. /* my socket forwarders - EMX lib only provides static forwarders */
  1143.  
  1144. static HMODULE htcp = 0;
  1145.  
  1146. static void *
  1147. tcp0(char *name)
  1148. {
  1149.     static BYTE buf[20];
  1150.     PFN fcn;
  1151.  
  1152.     if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
  1153.     if (!htcp)
  1154.     DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
  1155.     if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
  1156.     return (void *) ((void * (*)(void)) fcn) ();
  1157.     return 0;
  1158. }
  1159.  
  1160. static void
  1161. tcp1(char *name, int arg)
  1162. {
  1163.     static BYTE buf[20];
  1164.     PFN fcn;
  1165.  
  1166.     if (!(_emx_env & 0x200)) croak("%s requires OS/2", name); /* Die if not OS/2. */
  1167.     if (!htcp)
  1168.     DosLoadModule(buf, sizeof buf, "tcp32dll", &htcp);
  1169.     if (htcp && DosQueryProcAddr(htcp, 0, name, &fcn) == 0)
  1170.     ((void (*)(int)) fcn) (arg);
  1171. }
  1172.  
  1173. void *    gethostent()    { return tcp0("GETHOSTENT");  }
  1174. void *    getnetent()    { return tcp0("GETNETENT");   }
  1175. void *    getprotoent()    { return tcp0("GETPROTOENT"); }
  1176. void *    getservent()    { return tcp0("GETSERVENT");  }
  1177. void    sethostent(x)    { tcp1("SETHOSTENT",  x); }
  1178. void    setnetent(x)    { tcp1("SETNETENT",   x); }
  1179. void    setprotoent(x)    { tcp1("SETPROTOENT", x); }
  1180. void    setservent(x)    { tcp1("SETSERVENT",  x); }
  1181. void    endhostent()    { tcp0("ENDHOSTENT");  }
  1182. void    endnetent()    { tcp0("ENDNETENT");   }
  1183. void    endprotoent()    { tcp0("ENDPROTOENT"); }
  1184. void    endservent()    { tcp0("ENDSERVENT");  }
  1185.  
  1186. /*****************************************************************************/
  1187. /* not implemented in C Set++ */
  1188.  
  1189. #ifndef __EMX__
  1190. int    setuid(x)    { errno = EINVAL; return -1; }
  1191. int    setgid(x)    { errno = EINVAL; return -1; }
  1192. #endif
  1193.  
  1194. /*****************************************************************************/
  1195. /* stat() hack for char/block device */
  1196.  
  1197. #if OS2_STAT_HACK
  1198.  
  1199.     /* First attempt used DosQueryFSAttach which crashed the system when
  1200.        used with 5.001. Now just look for /dev/. */
  1201.  
  1202. int
  1203. os2_stat(char *name, struct stat *st)
  1204. {
  1205.     static int ino = SHRT_MAX;
  1206.  
  1207.     if (stricmp(name, "/dev/con") != 0
  1208.      && stricmp(name, "/dev/tty") != 0)
  1209.     return stat(name, st);
  1210.  
  1211.     memset(st, 0, sizeof *st);
  1212.     st->st_mode = S_IFCHR|0666;
  1213.     st->st_ino = (ino-- & 0x7FFF);
  1214.     st->st_nlink = 1;
  1215.     return 0;
  1216. }
  1217.  
  1218. #endif
  1219.  
  1220. #ifdef USE_PERL_SBRK
  1221.  
  1222. /* SBRK() emulation, mostly moved to malloc.c. */
  1223.  
  1224. void *
  1225. sys_alloc(int size) {
  1226.     void *got;
  1227.     APIRET rc = DosAllocMem(&got, size, PAG_COMMIT | PAG_WRITE);
  1228.  
  1229.     if (rc == ERROR_NOT_ENOUGH_MEMORY) {
  1230.     return (void *) -1;
  1231.     } else if ( rc ) 
  1232.     croak("Got an error from DosAllocMem: %li", (long)rc);
  1233.     return got;
  1234. }
  1235.  
  1236. #endif /* USE_PERL_SBRK */
  1237.  
  1238. /* tmp path */
  1239.  
  1240. char *tmppath = TMPPATH1;
  1241.  
  1242. void
  1243. settmppath()
  1244. {
  1245.     char *p = getenv("TMP"), *tpath;
  1246.     int len;
  1247.  
  1248.     if (!p) p = getenv("TEMP");
  1249.     if (!p) return;
  1250.     len = strlen(p);
  1251.     tpath = (char *)malloc(len + strlen(TMPPATH1) + 2);
  1252.     if (tpath) {
  1253.     strcpy(tpath, p);
  1254.     tpath[len] = '/';
  1255.     strcpy(tpath + len + 1, TMPPATH1);
  1256.     tmppath = tpath;
  1257.     }
  1258. }
  1259.  
  1260. #include "XSUB.h"
  1261.  
  1262. XS(XS_File__Copy_syscopy)
  1263. {
  1264.     dXSARGS;
  1265.     if (items < 2 || items > 3)
  1266.     croak("Usage: File::Copy::syscopy(src,dst,flag=0)");
  1267.     {
  1268.     STRLEN n_a;
  1269.     char *    src = (char *)SvPV(ST(0),n_a);
  1270.     char *    dst = (char *)SvPV(ST(1),n_a);
  1271.     U32    flag;
  1272.     int    RETVAL, rc;
  1273.  
  1274.     if (items < 3)
  1275.         flag = 0;
  1276.     else {
  1277.         flag = (unsigned long)SvIV(ST(2));
  1278.     }
  1279.  
  1280.     RETVAL = !CheckOSError(DosCopy(src, dst, flag));
  1281.     ST(0) = sv_newmortal();
  1282.     sv_setiv(ST(0), (IV)RETVAL);
  1283.     }
  1284.     XSRETURN(1);
  1285. }
  1286.  
  1287. #include "patchlevel.h"
  1288.  
  1289. char *
  1290. mod2fname(sv)
  1291.      SV   *sv;
  1292. {
  1293.     static char fname[9];
  1294.     int pos = 6, len, avlen;
  1295.     unsigned int sum = 0;
  1296.     AV  *av;
  1297.     SV  *svp;
  1298.     char *s;
  1299.     STRLEN n_a;
  1300.  
  1301.     if (!SvROK(sv)) croak("Not a reference given to mod2fname");
  1302.     sv = SvRV(sv);
  1303.     if (SvTYPE(sv) != SVt_PVAV) 
  1304.       croak("Not array reference given to mod2fname");
  1305.  
  1306.     avlen = av_len((AV*)sv);
  1307.     if (avlen < 0) 
  1308.       croak("Empty array reference given to mod2fname");
  1309.  
  1310.     s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
  1311.     strncpy(fname, s, 8);
  1312.     len = strlen(s);
  1313.     if (len < 6) pos = len;
  1314.     while (*s) {
  1315.     sum = 33 * sum + *(s++);    /* Checksumming first chars to
  1316.                      * get the capitalization into c.s. */
  1317.     }
  1318.     avlen --;
  1319.     while (avlen >= 0) {
  1320.     s = SvPV(*av_fetch((AV*)sv, avlen, FALSE), n_a);
  1321.     while (*s) {
  1322.         sum = 33 * sum + *(s++);    /* 7 is primitive mod 13. */
  1323.     }
  1324.     avlen --;
  1325.     }
  1326. #ifdef USE_THREADS
  1327.     sum++;                /* Avoid conflict of DLLs in memory. */
  1328. #endif 
  1329.     sum += PERL_VERSION * 200 + PERL_SUBVERSION * 2;  /*  */
  1330.     fname[pos] = 'A' + (sum % 26);
  1331.     fname[pos + 1] = 'A' + (sum / 26 % 26);
  1332.     fname[pos + 2] = '\0';
  1333.     return (char *)fname;
  1334. }
  1335.  
  1336. XS(XS_DynaLoader_mod2fname)
  1337. {
  1338.     dXSARGS;
  1339.     if (items != 1)
  1340.     croak("Usage: DynaLoader::mod2fname(sv)");
  1341.     {
  1342.     SV *    sv = ST(0);
  1343.     char *    RETVAL;
  1344.  
  1345.     RETVAL = mod2fname(sv);
  1346.     ST(0) = sv_newmortal();
  1347.     sv_setpv((SV*)ST(0), RETVAL);
  1348.     }
  1349.     XSRETURN(1);
  1350. }
  1351.  
  1352. char *
  1353. os2error(int rc)
  1354. {
  1355.     static char buf[300];
  1356.     ULONG len;
  1357.  
  1358.         if (!(_emx_env & 0x200)) return ""; /* Nop if not OS/2. */
  1359.     if (rc == 0)
  1360.         return NULL;
  1361.     if (DosGetMessage(NULL, 0, buf, sizeof buf - 1, rc, "OSO001.MSG", &len))
  1362.         sprintf(buf, "OS/2 system error code %d=0x%x", rc, rc);
  1363.     else {
  1364.         buf[len] = '\0';
  1365.         if (len && buf[len - 1] == '\n')
  1366.             buf[--len] = 0;
  1367.         if (len && buf[len - 1] == '\r')
  1368.             buf[--len] = 0;
  1369.         if (len && buf[len - 1] == '.')
  1370.             buf[--len] = 0;
  1371.     }
  1372.     return buf;
  1373. }
  1374.  
  1375. char *
  1376. os2_execname(void)
  1377. {
  1378.   char buf[300], *p;
  1379.  
  1380.   if (_execname(buf, sizeof buf) != 0)
  1381.     return PL_origargv[0];
  1382.   p = buf;
  1383.   while (*p) {
  1384.     if (*p == '\\')
  1385.     *p = '/';
  1386.     p++;
  1387.   }
  1388.   p = savepv(buf);
  1389.   SAVEFREEPV(p);
  1390.   return p;
  1391. }
  1392.  
  1393. char *
  1394. perllib_mangle(char *s, unsigned int l)
  1395. {
  1396.     static char *newp, *oldp;
  1397.     static int newl, oldl, notfound;
  1398.     static char ret[STATIC_FILE_LENGTH+1];
  1399.     
  1400.     if (!newp && !notfound) {
  1401.     newp = getenv("PERLLIB_PREFIX");
  1402.     if (newp) {
  1403.         char *s;
  1404.         
  1405.         oldp = newp;
  1406.         while (*newp && !isSPACE(*newp) && *newp != ';') {
  1407.         newp++; oldl++;        /* Skip digits. */
  1408.         }
  1409.         while (*newp && (isSPACE(*newp) || *newp == ';')) {
  1410.         newp++;            /* Skip whitespace. */
  1411.         }
  1412.         newl = strlen(newp);
  1413.         if (newl == 0 || oldl == 0) {
  1414.         croak("Malformed PERLLIB_PREFIX");
  1415.         }
  1416.         strcpy(ret, newp);
  1417.         s = ret;
  1418.         while (*s) {
  1419.         if (*s == '\\') *s = '/';
  1420.         s++;
  1421.         }
  1422.     } else {
  1423.         notfound = 1;
  1424.     }
  1425.     }
  1426.     if (!newp) {
  1427.     return s;
  1428.     }
  1429.     if (l == 0) {
  1430.     l = strlen(s);
  1431.     }
  1432.     if (l < oldl || strnicmp(oldp, s, oldl) != 0) {
  1433.     return s;
  1434.     }
  1435.     if (l + newl - oldl > STATIC_FILE_LENGTH || newl > STATIC_FILE_LENGTH) {
  1436.     croak("Malformed PERLLIB_PREFIX");
  1437.     }
  1438.     strcpy(ret + newl, s + oldl);
  1439.     return ret;
  1440. }
  1441.  
  1442. unsigned long 
  1443. Perl_hab_GET()            /* Needed if perl.h cannot be included */
  1444. {
  1445.     return perl_hab_GET();
  1446. }
  1447.  
  1448. HMQ
  1449. Perl_Register_MQ(int serve)
  1450. {
  1451.     PPIB pib;
  1452.     PTIB tib;
  1453.  
  1454.     if (Perl_os2_initial_mode++)
  1455.     return Perl_hmq;
  1456.     DosGetInfoBlocks(&tib, &pib);
  1457.     Perl_os2_initial_mode = pib->pib_ultype;
  1458.     Perl_hmq_refcnt = 1;
  1459.     /* Try morphing into a PM application. */
  1460.     if (pib->pib_ultype != 3)        /* 2 is VIO */
  1461.     pib->pib_ultype = 3;        /* 3 is PM */
  1462.     init_PMWIN_entries();
  1463.     /* 64 messages if before OS/2 3.0, ignored otherwise */
  1464.     Perl_hmq = (*PMWIN_entries.CreateMsgQueue)(perl_hab_GET(), 64); 
  1465.     if (!Perl_hmq) {
  1466.     static int cnt;
  1467.     if (cnt++)
  1468.         _exit(188);            /* Panic can try to create a window. */
  1469.     croak("Cannot create a message queue, or morph to a PM application");
  1470.     }
  1471.     return Perl_hmq;
  1472. }
  1473.  
  1474. int
  1475. Perl_Serve_Messages(int force)
  1476. {
  1477.     int cnt = 0;
  1478.     QMSG msg;
  1479.  
  1480.     if (Perl_hmq_servers && !force)
  1481.     return 0;
  1482.     if (!Perl_hmq_refcnt)
  1483.     croak("No message queue");
  1484.     while ((*PMWIN_entries.PeekMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0, PM_REMOVE)) {
  1485.     cnt++;
  1486.     if (msg.msg == WM_QUIT)
  1487.         croak("QUITing...");
  1488.     (*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
  1489.     }
  1490.     return cnt;
  1491. }
  1492.  
  1493. int
  1494. Perl_Process_Messages(int force, I32 *cntp)
  1495. {
  1496.     QMSG msg;
  1497.  
  1498.     if (Perl_hmq_servers && !force)
  1499.     return 0;
  1500.     if (!Perl_hmq_refcnt)
  1501.     croak("No message queue");
  1502.     while ((*PMWIN_entries.GetMsg)(Perl_hab, &msg, NULLHANDLE, 0, 0)) {
  1503.     if (cntp)
  1504.         (*cntp)++;
  1505.     (*PMWIN_entries.DispatchMsg)(Perl_hab, &msg);
  1506.     if (msg.msg == WM_DESTROY)
  1507.         return -1;
  1508.     if (msg.msg == WM_CREATE)
  1509.         return +1;
  1510.     }
  1511.     croak("QUITing...");
  1512. }
  1513.  
  1514. void
  1515. Perl_Deregister_MQ(int serve)
  1516. {
  1517.     PPIB pib;
  1518.     PTIB tib;
  1519.  
  1520.     if (--Perl_hmq_refcnt == 0) {
  1521.     (*PMWIN_entries.DestroyMsgQueue)(Perl_hmq);
  1522.     Perl_hmq = 0;
  1523.     /* Try morphing back from a PM application. */
  1524.     if (pib->pib_ultype == 3)        /* 3 is PM */
  1525.         pib->pib_ultype = Perl_os2_initial_mode;
  1526.     else
  1527.         warn("Unexpected program mode %d when morphing back from PM",
  1528.          pib->pib_ultype);
  1529.     }
  1530. }
  1531.  
  1532. extern void dlopen();
  1533. void *fakedl = &dlopen;        /* Pull in dynaloading part. */
  1534.  
  1535. #define sys_is_absolute(path) ( isALPHA((path)[0]) && (path)[1] == ':' \
  1536.                 && ((path)[2] == '/' || (path)[2] == '\\'))
  1537. #define sys_is_rooted _fnisabs
  1538. #define sys_is_relative _fnisrel
  1539. #define current_drive _getdrive
  1540.  
  1541. #undef chdir                /* Was _chdir2. */
  1542. #define sys_chdir(p) (chdir(p) == 0)
  1543. #define change_drive(d) (_chdrive(d), (current_drive() == toupper(d)))
  1544.  
  1545. static int DOS_harderr_state = -1;    
  1546.  
  1547. XS(XS_OS2_Error)
  1548. {
  1549.     dXSARGS;
  1550.     if (items != 2)
  1551.     croak("Usage: OS2::Error(harderr, exception)");
  1552.     {
  1553.     int    arg1 = SvIV(ST(0));
  1554.     int    arg2 = SvIV(ST(1));
  1555.     int    a = ((arg1 ? FERR_ENABLEHARDERR : FERR_DISABLEHARDERR)
  1556.              | (arg2 ? FERR_ENABLEEXCEPTION : FERR_DISABLEEXCEPTION));
  1557.     int    RETVAL = ((arg1 ? 1 : 0) | (arg2 ? 2 : 0));
  1558.     unsigned long rc;
  1559.  
  1560.     if (CheckOSError(DosError(a)))
  1561.         croak("DosError(%d) failed", a);
  1562.     ST(0) = sv_newmortal();
  1563.     if (DOS_harderr_state >= 0)
  1564.         sv_setiv(ST(0), DOS_harderr_state);
  1565.     DOS_harderr_state = RETVAL;
  1566.     }
  1567.     XSRETURN(1);
  1568. }
  1569.  
  1570. static signed char DOS_suppression_state = -1;    
  1571.  
  1572. XS(XS_OS2_Errors2Drive)
  1573. {
  1574.     dXSARGS;
  1575.     if (items != 1)
  1576.     croak("Usage: OS2::Errors2Drive(drive)");
  1577.     {
  1578.     STRLEN n_a;
  1579.     SV  *sv = ST(0);
  1580.     int    suppress = SvOK(sv);
  1581.     char    *s = suppress ? SvPV(sv, n_a) : NULL;
  1582.     char    drive = (s ? *s : 0);
  1583.     unsigned long rc;
  1584.  
  1585.     if (suppress && !isALPHA(drive))
  1586.         croak("Non-char argument '%c' to OS2::Errors2Drive()", drive);
  1587.     if (CheckOSError(DosSuppressPopUps((suppress
  1588.                         ? SPU_ENABLESUPPRESSION 
  1589.                         : SPU_DISABLESUPPRESSION),
  1590.                        drive)))
  1591.         croak("DosSuppressPopUps(%c) failed", drive);
  1592.     ST(0) = sv_newmortal();
  1593.     if (DOS_suppression_state > 0)
  1594.         sv_setpvn(ST(0), &DOS_suppression_state, 1);
  1595.     else if (DOS_suppression_state == 0)
  1596.         sv_setpvn(ST(0), "", 0);
  1597.     DOS_suppression_state = drive;
  1598.     }
  1599.     XSRETURN(1);
  1600. }
  1601.  
  1602. static const char * const si_fields[QSV_MAX] = {
  1603.   "MAX_PATH_LENGTH",
  1604.   "MAX_TEXT_SESSIONS",
  1605.   "MAX_PM_SESSIONS",
  1606.   "MAX_VDM_SESSIONS",
  1607.   "BOOT_DRIVE",
  1608.   "DYN_PRI_VARIATION",
  1609.   "MAX_WAIT",
  1610.   "MIN_SLICE",
  1611.   "MAX_SLICE",
  1612.   "PAGE_SIZE",
  1613.   "VERSION_MAJOR",
  1614.   "VERSION_MINOR",
  1615.   "VERSION_REVISION",
  1616.   "MS_COUNT",
  1617.   "TIME_LOW",
  1618.   "TIME_HIGH",
  1619.   "TOTPHYSMEM",
  1620.   "TOTRESMEM",
  1621.   "TOTAVAILMEM",
  1622.   "MAXPRMEM",
  1623.   "MAXSHMEM",
  1624.   "TIMER_INTERVAL",
  1625.   "MAX_COMP_LENGTH",
  1626.   "FOREGROUND_FS_SESSION",
  1627.   "FOREGROUND_PROCESS"
  1628. };
  1629.  
  1630. XS(XS_OS2_SysInfo)
  1631. {
  1632.     dXSARGS;
  1633.     if (items != 0)
  1634.     croak("Usage: OS2::SysInfo()");
  1635.     {
  1636.     ULONG   si[QSV_MAX] = {0};    /* System Information Data Buffer */
  1637.     APIRET  rc    = NO_ERROR;    /* Return code            */
  1638.     int i = 0, j = 0;
  1639.  
  1640.     if (CheckOSError(DosQuerySysInfo(1L, /* Request all available system */
  1641.                      QSV_MAX, /* information */
  1642.                      (PVOID)si,
  1643.                      sizeof(si))))
  1644.         croak("DosQuerySysInfo() failed");
  1645.     EXTEND(SP,2*QSV_MAX);
  1646.     while (i < QSV_MAX) {
  1647.         ST(j) = sv_newmortal();
  1648.         sv_setpv(ST(j++), si_fields[i]);
  1649.         ST(j) = sv_newmortal();
  1650.         sv_setiv(ST(j++), si[i]);
  1651.         i++;
  1652.     }
  1653.     }
  1654.     XSRETURN(2 * QSV_MAX);
  1655. }
  1656.  
  1657. XS(XS_OS2_BootDrive)
  1658. {
  1659.     dXSARGS;
  1660.     if (items != 0)
  1661.     croak("Usage: OS2::BootDrive()");
  1662.     {
  1663.     ULONG   si[1] = {0};    /* System Information Data Buffer */
  1664.     APIRET  rc    = NO_ERROR;    /* Return code            */
  1665.     char c;
  1666.     
  1667.     if (CheckOSError(DosQuerySysInfo(QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
  1668.                      (PVOID)si, sizeof(si))))
  1669.         croak("DosQuerySysInfo() failed");
  1670.     ST(0) = sv_newmortal();
  1671.     c = 'a' - 1 + si[0];
  1672.     sv_setpvn(ST(0), &c, 1);
  1673.     }
  1674.     XSRETURN(1);
  1675. }
  1676.  
  1677. XS(XS_OS2_MorphPM)
  1678. {
  1679.     dXSARGS;
  1680.     if (items != 1)
  1681.     croak("Usage: OS2::MorphPM(serve)");
  1682.     {
  1683.     bool  serve = SvOK(ST(0));
  1684.     unsigned long   pmq = perl_hmq_GET(serve);
  1685.  
  1686.     ST(0) = sv_newmortal();
  1687.     sv_setiv(ST(0), pmq);
  1688.     }
  1689.     XSRETURN(1);
  1690. }
  1691.  
  1692. XS(XS_OS2_UnMorphPM)
  1693. {
  1694.     dXSARGS;
  1695.     if (items != 1)
  1696.     croak("Usage: OS2::UnMorphPM(serve)");
  1697.     {
  1698.     bool  serve = SvOK(ST(0));
  1699.  
  1700.     perl_hmq_UNSET(serve);
  1701.     }
  1702.     XSRETURN(0);
  1703. }
  1704.  
  1705. XS(XS_OS2_Serve_Messages)
  1706. {
  1707.     dXSARGS;
  1708.     if (items != 1)
  1709.     croak("Usage: OS2::Serve_Messages(force)");
  1710.     {
  1711.     bool  force = SvOK(ST(0));
  1712.     unsigned long   cnt = Perl_Serve_Messages(force);
  1713.  
  1714.     ST(0) = sv_newmortal();
  1715.     sv_setiv(ST(0), cnt);
  1716.     }
  1717.     XSRETURN(1);
  1718. }
  1719.  
  1720. XS(XS_OS2_Process_Messages)
  1721. {
  1722.     dXSARGS;
  1723.     if (items < 1 || items > 2)
  1724.     croak("Usage: OS2::Process_Messages(force [, cnt])");
  1725.     {
  1726.     bool  force = SvOK(ST(0));
  1727.     unsigned long   cnt;
  1728.     I32 *cntp = NULL;
  1729.  
  1730.     if (items == 2) {
  1731.         SV *sv = ST(1);
  1732.         int fake = SvIV(sv);    /* Force SvIVX */
  1733.         
  1734.         if (!SvIOK(sv))
  1735.         croak("Can't upgrade count to IV");
  1736.         cntp = &SvIVX(sv);
  1737.     }
  1738.     cnt =  Perl_Process_Messages(force, cntp);
  1739.     ST(0) = sv_newmortal();
  1740.     sv_setiv(ST(0), cnt);
  1741.     }
  1742.     XSRETURN(1);
  1743. }
  1744.  
  1745. XS(XS_Cwd_current_drive)
  1746. {
  1747.     dXSARGS;
  1748.     if (items != 0)
  1749.     croak("Usage: Cwd::current_drive()");
  1750.     {
  1751.     char    RETVAL;
  1752.  
  1753.     RETVAL = current_drive();
  1754.     ST(0) = sv_newmortal();
  1755.     sv_setpvn(ST(0), (char *)&RETVAL, 1);
  1756.     }
  1757.     XSRETURN(1);
  1758. }
  1759.  
  1760. XS(XS_Cwd_sys_chdir)
  1761. {
  1762.     dXSARGS;
  1763.     if (items != 1)
  1764.     croak("Usage: Cwd::sys_chdir(path)");
  1765.     {
  1766.     STRLEN n_a;
  1767.     char *    path = (char *)SvPV(ST(0),n_a);
  1768.     bool    RETVAL;
  1769.  
  1770.     RETVAL = sys_chdir(path);
  1771.     ST(0) = boolSV(RETVAL);
  1772.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  1773.     }
  1774.     XSRETURN(1);
  1775. }
  1776.  
  1777. XS(XS_Cwd_change_drive)
  1778. {
  1779.     dXSARGS;
  1780.     if (items != 1)
  1781.     croak("Usage: Cwd::change_drive(d)");
  1782.     {
  1783.     STRLEN n_a;
  1784.     char    d = (char)*SvPV(ST(0),n_a);
  1785.     bool    RETVAL;
  1786.  
  1787.     RETVAL = change_drive(d);
  1788.     ST(0) = boolSV(RETVAL);
  1789.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  1790.     }
  1791.     XSRETURN(1);
  1792. }
  1793.  
  1794. XS(XS_Cwd_sys_is_absolute)
  1795. {
  1796.     dXSARGS;
  1797.     if (items != 1)
  1798.     croak("Usage: Cwd::sys_is_absolute(path)");
  1799.     {
  1800.     STRLEN n_a;
  1801.     char *    path = (char *)SvPV(ST(0),n_a);
  1802.     bool    RETVAL;
  1803.  
  1804.     RETVAL = sys_is_absolute(path);
  1805.     ST(0) = boolSV(RETVAL);
  1806.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  1807.     }
  1808.     XSRETURN(1);
  1809. }
  1810.  
  1811. XS(XS_Cwd_sys_is_rooted)
  1812. {
  1813.     dXSARGS;
  1814.     if (items != 1)
  1815.     croak("Usage: Cwd::sys_is_rooted(path)");
  1816.     {
  1817.     STRLEN n_a;
  1818.     char *    path = (char *)SvPV(ST(0),n_a);
  1819.     bool    RETVAL;
  1820.  
  1821.     RETVAL = sys_is_rooted(path);
  1822.     ST(0) = boolSV(RETVAL);
  1823.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  1824.     }
  1825.     XSRETURN(1);
  1826. }
  1827.  
  1828. XS(XS_Cwd_sys_is_relative)
  1829. {
  1830.     dXSARGS;
  1831.     if (items != 1)
  1832.     croak("Usage: Cwd::sys_is_relative(path)");
  1833.     {
  1834.     STRLEN n_a;
  1835.     char *    path = (char *)SvPV(ST(0),n_a);
  1836.     bool    RETVAL;
  1837.  
  1838.     RETVAL = sys_is_relative(path);
  1839.     ST(0) = boolSV(RETVAL);
  1840.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  1841.     }
  1842.     XSRETURN(1);
  1843. }
  1844.  
  1845. XS(XS_Cwd_sys_cwd)
  1846. {
  1847.     dXSARGS;
  1848.     if (items != 0)
  1849.     croak("Usage: Cwd::sys_cwd()");
  1850.     {
  1851.     char p[MAXPATHLEN];
  1852.     char *    RETVAL;
  1853.     RETVAL = _getcwd2(p, MAXPATHLEN);
  1854.     ST(0) = sv_newmortal();
  1855.     sv_setpv((SV*)ST(0), RETVAL);
  1856.     }
  1857.     XSRETURN(1);
  1858. }
  1859.  
  1860. XS(XS_Cwd_sys_abspath)
  1861. {
  1862.     dXSARGS;
  1863.     if (items < 1 || items > 2)
  1864.     croak("Usage: Cwd::sys_abspath(path, dir = NULL)");
  1865.     {
  1866.     STRLEN n_a;
  1867.     char *    path = (char *)SvPV(ST(0),n_a);
  1868.     char *    dir;
  1869.     char p[MAXPATHLEN];
  1870.     char *    RETVAL;
  1871.  
  1872.     if (items < 2)
  1873.         dir = NULL;
  1874.     else {
  1875.         dir = (char *)SvPV(ST(1),n_a);
  1876.     }
  1877.     if (path[0] == '.' && (path[1] == '/' || path[1] == '\\')) {
  1878.         path += 2;
  1879.     }
  1880.     if (dir == NULL) {
  1881.         if (_abspath(p, path, MAXPATHLEN) == 0) {
  1882.         RETVAL = p;
  1883.         } else {
  1884.         RETVAL = NULL;
  1885.         }
  1886.     } else {
  1887.         /* Absolute with drive: */
  1888.         if ( sys_is_absolute(path) ) {
  1889.         if (_abspath(p, path, MAXPATHLEN) == 0) {
  1890.             RETVAL = p;
  1891.         } else {
  1892.             RETVAL = NULL;
  1893.         }
  1894.         } else if (path[0] == '/' || path[0] == '\\') {
  1895.         /* Rooted, but maybe on different drive. */
  1896.         if (isALPHA(dir[0]) && dir[1] == ':' ) {
  1897.             char p1[MAXPATHLEN];
  1898.  
  1899.             /* Need to prepend the drive. */
  1900.             p1[0] = dir[0];
  1901.             p1[1] = dir[1];
  1902.             Copy(path, p1 + 2, strlen(path) + 1, char);
  1903.             RETVAL = p;
  1904.             if (_abspath(p, p1, MAXPATHLEN) == 0) {
  1905.             RETVAL = p;
  1906.             } else {
  1907.             RETVAL = NULL;
  1908.             }
  1909.         } else if (_abspath(p, path, MAXPATHLEN) == 0) {
  1910.             RETVAL = p;
  1911.         } else {
  1912.             RETVAL = NULL;
  1913.         }
  1914.         } else {
  1915.         /* Either path is relative, or starts with a drive letter. */
  1916.         /* If the path starts with a drive letter, then dir is
  1917.            relevant only if 
  1918.            a/b)    it is absolute/x:relative on the same drive.  
  1919.            c)    path is on current drive, and dir is rooted
  1920.            In all the cases it is safe to drop the drive part
  1921.            of the path. */
  1922.         if ( !sys_is_relative(path) ) {
  1923.             int is_drived;
  1924.  
  1925.             if ( ( ( sys_is_absolute(dir)
  1926.                  || (isALPHA(dir[0]) && dir[1] == ':' 
  1927.                  && strnicmp(dir, path,1) == 0)) 
  1928.                && strnicmp(dir, path,1) == 0)
  1929.              || ( !(isALPHA(dir[0]) && dir[1] == ':')
  1930.                   && toupper(path[0]) == current_drive())) {
  1931.             path += 2;
  1932.             } else if (_abspath(p, path, MAXPATHLEN) == 0) {
  1933.             RETVAL = p; goto done;
  1934.             } else {
  1935.             RETVAL = NULL; goto done;
  1936.             }
  1937.         }
  1938.         {
  1939.             /* Need to prepend the absolute path of dir. */
  1940.             char p1[MAXPATHLEN];
  1941.  
  1942.             if (_abspath(p1, dir, MAXPATHLEN) == 0) {
  1943.             int l = strlen(p1);
  1944.  
  1945.             if (p1[ l - 1 ] != '/') {
  1946.                 p1[ l ] = '/';
  1947.                 l++;
  1948.             }
  1949.             Copy(path, p1 + l, strlen(path) + 1, char);
  1950.             if (_abspath(p, p1, MAXPATHLEN) == 0) {
  1951.                 RETVAL = p;
  1952.             } else {
  1953.                 RETVAL = NULL;
  1954.             }
  1955.             } else {
  1956.             RETVAL = NULL;
  1957.             }
  1958.         }
  1959.           done:
  1960.         }
  1961.     }
  1962.     ST(0) = sv_newmortal();
  1963.     sv_setpv((SV*)ST(0), RETVAL);
  1964.     }
  1965.     XSRETURN(1);
  1966. }
  1967. typedef APIRET (*PELP)(PSZ path, ULONG type);
  1968.  
  1969. APIRET
  1970. ExtLIBPATH(ULONG ord, PSZ path, ULONG type)
  1971. {
  1972.     loadByOrd("doscalls",ord);        /* Guarantied to load or die! */
  1973.     return (*(PELP)ExtFCN[ord])(path, type);
  1974. }
  1975.  
  1976. #define extLibpath(type)                         \
  1977.     (CheckOSError(ExtLIBPATH(ORD_QUERY_ELP, to, ((type) ? END_LIBPATH    \
  1978.                          : BEGIN_LIBPATH)))    \
  1979.      ? NULL : to )
  1980.  
  1981. #define extLibpath_set(p,type)                     \
  1982.     (!CheckOSError(ExtLIBPATH(ORD_SET_ELP, (p), ((type) ? END_LIBPATH    \
  1983.                          : BEGIN_LIBPATH))))
  1984.  
  1985. XS(XS_Cwd_extLibpath)
  1986. {
  1987.     dXSARGS;
  1988.     if (items < 0 || items > 1)
  1989.     croak("Usage: Cwd::extLibpath(type = 0)");
  1990.     {
  1991.     bool    type;
  1992.     char    to[1024];
  1993.     U32    rc;
  1994.     char *    RETVAL;
  1995.  
  1996.     if (items < 1)
  1997.         type = 0;
  1998.     else {
  1999.         type = (int)SvIV(ST(0));
  2000.     }
  2001.  
  2002.     RETVAL = extLibpath(type);
  2003.     ST(0) = sv_newmortal();
  2004.     sv_setpv((SV*)ST(0), RETVAL);
  2005.     }
  2006.     XSRETURN(1);
  2007. }
  2008.  
  2009. XS(XS_Cwd_extLibpath_set)
  2010. {
  2011.     dXSARGS;
  2012.     if (items < 1 || items > 2)
  2013.     croak("Usage: Cwd::extLibpath_set(s, type = 0)");
  2014.     {
  2015.     STRLEN n_a;
  2016.     char *    s = (char *)SvPV(ST(0),n_a);
  2017.     bool    type;
  2018.     U32    rc;
  2019.     bool    RETVAL;
  2020.  
  2021.     if (items < 2)
  2022.         type = 0;
  2023.     else {
  2024.         type = (int)SvIV(ST(1));
  2025.     }
  2026.  
  2027.     RETVAL = extLibpath_set(s, type);
  2028.     ST(0) = boolSV(RETVAL);
  2029.     if (SvREFCNT(ST(0))) sv_2mortal(ST(0));
  2030.     }
  2031.     XSRETURN(1);
  2032. }
  2033.  
  2034. int
  2035. Xs_OS2_init()
  2036. {
  2037.     char *file = __FILE__;
  2038.     {
  2039.     GV *gv;
  2040.  
  2041.     if (_emx_env & 0x200) {    /* OS/2 */
  2042.             newXS("File::Copy::syscopy", XS_File__Copy_syscopy, file);
  2043.             newXS("Cwd::extLibpath", XS_Cwd_extLibpath, file);
  2044.             newXS("Cwd::extLibpath_set", XS_Cwd_extLibpath_set, file);
  2045.     }
  2046.         newXS("OS2::Error", XS_OS2_Error, file);
  2047.         newXS("OS2::Errors2Drive", XS_OS2_Errors2Drive, file);
  2048.         newXS("OS2::SysInfo", XS_OS2_SysInfo, file);
  2049.         newXS("OS2::BootDrive", XS_OS2_BootDrive, file);
  2050.         newXS("OS2::MorphPM", XS_OS2_MorphPM, file);
  2051.         newXS("OS2::UnMorphPM", XS_OS2_UnMorphPM, file);
  2052.         newXS("OS2::Serve_Messages", XS_OS2_Serve_Messages, file);
  2053.         newXS("OS2::Process_Messages", XS_OS2_Process_Messages, file);
  2054.         newXS("DynaLoader::mod2fname", XS_DynaLoader_mod2fname, file);
  2055.         newXS("Cwd::current_drive", XS_Cwd_current_drive, file);
  2056.         newXS("Cwd::sys_chdir", XS_Cwd_sys_chdir, file);
  2057.         newXS("Cwd::change_drive", XS_Cwd_change_drive, file);
  2058.         newXS("Cwd::sys_is_absolute", XS_Cwd_sys_is_absolute, file);
  2059.         newXS("Cwd::sys_is_rooted", XS_Cwd_sys_is_rooted, file);
  2060.         newXS("Cwd::sys_is_relative", XS_Cwd_sys_is_relative, file);
  2061.         newXS("Cwd::sys_cwd", XS_Cwd_sys_cwd, file);
  2062.         newXS("Cwd::sys_abspath", XS_Cwd_sys_abspath, file);
  2063.     gv = gv_fetchpv("OS2::is_aout", TRUE, SVt_PV);
  2064.     GvMULTI_on(gv);
  2065. #ifdef PERL_IS_AOUT
  2066.     sv_setiv(GvSV(gv), 1);
  2067. #endif 
  2068.     gv = gv_fetchpv("OS2::emx_rev", TRUE, SVt_PV);
  2069.     GvMULTI_on(gv);
  2070.     sv_setiv(GvSV(gv), _emx_rev);
  2071.     sv_setpv(GvSV(gv), _emx_vprt);
  2072.     SvIOK_on(GvSV(gv));
  2073.     gv = gv_fetchpv("OS2::emx_env", TRUE, SVt_PV);
  2074.     GvMULTI_on(gv);
  2075.     sv_setiv(GvSV(gv), _emx_env);
  2076.     gv = gv_fetchpv("OS2::os_ver", TRUE, SVt_PV);
  2077.     GvMULTI_on(gv);
  2078.     sv_setnv(GvSV(gv), _osmajor + 0.001 * _osminor);
  2079.     }
  2080. }
  2081.  
  2082. OS2_Perl_data_t OS2_Perl_data;
  2083.  
  2084. void
  2085. Perl_OS2_init(char **env)
  2086. {
  2087.     char *shell;
  2088.  
  2089.     MALLOC_INIT;
  2090.     settmppath();
  2091.     OS2_Perl_data.xs_init = &Xs_OS2_init;
  2092.     _uflags (_UF_SBRK_MODEL, _UF_SBRK_ARBITRARY);
  2093.     if (environ == NULL && env) {
  2094.     environ = env;
  2095.     }
  2096.     if ( (shell = getenv("PERL_SH_DRIVE")) ) {
  2097.     New(1304, PL_sh_path, strlen(SH_PATH) + 1, char);
  2098.     strcpy(PL_sh_path, SH_PATH);
  2099.     PL_sh_path[0] = shell[0];
  2100.     } else if ( (shell = getenv("PERL_SH_DIR")) ) {
  2101.     int l = strlen(shell), i;
  2102.     if (shell[l-1] == '/' || shell[l-1] == '\\') {
  2103.         l--;
  2104.     }
  2105.     New(1304, PL_sh_path, l + 8, char);
  2106.     strncpy(PL_sh_path, shell, l);
  2107.     strcpy(PL_sh_path + l, "/sh.exe");
  2108.     for (i = 0; i < l; i++) {
  2109.         if (PL_sh_path[i] == '\\') PL_sh_path[i] = '/';
  2110.     }
  2111.     }
  2112.     MUTEX_INIT(&start_thread_mutex);
  2113.     os2_mytype = my_type();        /* Do it before morphing.  Needed? */
  2114. }
  2115.  
  2116. #undef tmpnam
  2117. #undef tmpfile
  2118.  
  2119. char *
  2120. my_tmpnam (char *str)
  2121. {
  2122.     char *p = getenv("TMP"), *tpath;
  2123.     int len;
  2124.  
  2125.     if (!p) p = getenv("TEMP");
  2126.     tpath = tempnam(p, "pltmp");
  2127.     if (str && tpath) {
  2128.     strcpy(str, tpath);
  2129.     return str;
  2130.     }
  2131.     return tpath;
  2132. }
  2133.  
  2134. FILE *
  2135. my_tmpfile ()
  2136. {
  2137.     struct stat s;
  2138.  
  2139.     stat(".", &s);
  2140.     if (s.st_mode & S_IWOTH) {
  2141.     return tmpfile();
  2142.     }
  2143.     return fopen(my_tmpnam(NULL), "w+b"); /* Race condition, but
  2144.                          grants TMP. */
  2145. }
  2146.  
  2147. #undef flock
  2148.  
  2149. /* This code was contributed by Rocco Caputo. */
  2150. int 
  2151. my_flock(int handle, int o)
  2152. {
  2153.   FILELOCK      rNull, rFull;
  2154.   ULONG         timeout, handle_type, flag_word;
  2155.   APIRET        rc;
  2156.   int           blocking, shared;
  2157.   static int    use_my = -1;
  2158.  
  2159.   if (use_my == -1) {
  2160.     char *s = getenv("USE_PERL_FLOCK");
  2161.     if (s)
  2162.     use_my = atoi(s);
  2163.     else 
  2164.     use_my = 1;
  2165.   }
  2166.   if (!(_emx_env & 0x200) || !use_my) 
  2167.     return flock(handle, o);    /* Delegate to EMX. */
  2168.   
  2169.                                         // is this a file?
  2170.   if ((DosQueryHType(handle, &handle_type, &flag_word) != 0) ||
  2171.       (handle_type & 0xFF))
  2172.   {
  2173.     errno = EBADF;
  2174.     return -1;
  2175.   }
  2176.                                         // set lock/unlock ranges
  2177.   rNull.lOffset = rNull.lRange = rFull.lOffset = 0;
  2178.   rFull.lRange = 0x7FFFFFFF;
  2179.                                         // set timeout for blocking
  2180.   timeout = ((blocking = !(o & LOCK_NB))) ? 100 : 1;
  2181.                                         // shared or exclusive?
  2182.   shared = (o & LOCK_SH) ? 1 : 0;
  2183.                                         // do not block the unlock
  2184.   if (o & (LOCK_UN | LOCK_SH | LOCK_EX)) {
  2185.     rc = DosSetFileLocks(handle, &rFull, &rNull, timeout, shared);
  2186.     switch (rc) {
  2187.       case 0:
  2188.         errno = 0;
  2189.         return 0;
  2190.       case ERROR_INVALID_HANDLE:
  2191.         errno = EBADF;
  2192.         return -1;
  2193.       case ERROR_SHARING_BUFFER_EXCEEDED:
  2194.         errno = ENOLCK;
  2195.         return -1;
  2196.       case ERROR_LOCK_VIOLATION:
  2197.         break;                          // not an error
  2198.       case ERROR_INVALID_PARAMETER:
  2199.       case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
  2200.       case ERROR_READ_LOCKS_NOT_SUPPORTED:
  2201.         errno = EINVAL;
  2202.         return -1;
  2203.       case ERROR_INTERRUPT:
  2204.         errno = EINTR;
  2205.         return -1;
  2206.       default:
  2207.         errno = EINVAL;
  2208.         return -1;
  2209.     }
  2210.   }
  2211.                                         // lock may block
  2212.   if (o & (LOCK_SH | LOCK_EX)) {
  2213.                                         // for blocking operations
  2214.     for (;;) {
  2215.       rc =
  2216.         DosSetFileLocks(
  2217.                 handle,
  2218.                 &rNull,
  2219.                 &rFull,
  2220.                 timeout,
  2221.                 shared
  2222.         );
  2223.       switch (rc) {
  2224.         case 0:
  2225.           errno = 0;
  2226.           return 0;
  2227.         case ERROR_INVALID_HANDLE:
  2228.           errno = EBADF;
  2229.           return -1;
  2230.         case ERROR_SHARING_BUFFER_EXCEEDED:
  2231.           errno = ENOLCK;
  2232.           return -1;
  2233.         case ERROR_LOCK_VIOLATION:
  2234.           if (!blocking) {
  2235.             errno = EWOULDBLOCK;
  2236.             return -1;
  2237.           }
  2238.           break;
  2239.         case ERROR_INVALID_PARAMETER:
  2240.         case ERROR_ATOMIC_LOCK_NOT_SUPPORTED:
  2241.         case ERROR_READ_LOCKS_NOT_SUPPORTED:
  2242.           errno = EINVAL;
  2243.           return -1;
  2244.         case ERROR_INTERRUPT:
  2245.           errno = EINTR;
  2246.           return -1;
  2247.         default:
  2248.           errno = EINVAL;
  2249.           return -1;
  2250.       }
  2251.                                         // give away timeslice
  2252.       DosSleep(1);
  2253.     }
  2254.   }
  2255.  
  2256.   errno = 0;
  2257.   return 0;
  2258. }
  2259.