1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/msdos/utils.cpp
3// Purpose:     DOS implementations of utility functions
4// Author:      Vaclav Slavik, M.J.Wetherell
5// Id:          $Id: utilsdos.cpp 41020 2006-09-05 20:47:48Z VZ $
6// Copyright:   (c) 2001-2002 SciTech Software, Inc. (www.scitechsoft.com)
7//              (c) 2005 M.J.Wetherell
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// For compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#ifdef __BORLANDC__
15    #pragma hdrstop
16#endif
17
18#include "wx/utils.h"
19
20#ifndef WX_PRECOMP
21    #include "wx/string.h"
22    #include "wx/intl.h"
23    #include "wx/log.h"
24    #include "wx/app.h"
25#endif
26
27#include "wx/apptrait.h"
28#include "wx/process.h"
29#include "wx/confbase.h"        // for wxExpandEnvVars()
30#include "wx/cmdline.h"
31#include "wx/filename.h"
32#include "wx/wfstream.h"
33
34#include <stdarg.h>
35#include <string.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <unistd.h>
39#include <signal.h>
40#include <time.h>
41#include <dos.h>
42#include <process.h>
43
44//----------------------------------------------------------------------------
45// Sleep
46//----------------------------------------------------------------------------
47
48void wxSleep(int nSecs)
49{
50    wxMilliSleep(1000 * nSecs);
51}
52
53void wxMilliSleep(unsigned long milliseconds)
54{
55#if HAVE_USLEEP || defined __DJGPP__
56    usleep(milliseconds * 1000);
57#elif defined __WATCOMC__
58    delay(milliseconds);
59#else
60    clock_t start = clock();
61    while ((clock() - start) * 1000 / CLOCKS_PER_SEC < (clock_t)milliseconds)
62    {
63        // yield if in a multitasking environment
64        // "Release Current Virtual Machine's Time Slice" in DPMI 1.0
65        REGS r;
66        memset(&r, 0, sizeof(r));
67        r.x.ax = 0x1680;
68        int386(0x2f, &r, &r);
69    }
70#endif
71}
72
73void wxMicroSleep(unsigned long microseconds)
74{
75#if HAVE_USLEEP || defined __DJGPP__
76    usleep(microseconds);
77#else
78    wxMilliSleep(microseconds/1000);
79#endif
80}
81
82//----------------------------------------------------------------------------
83// Get/Set environment variables
84//----------------------------------------------------------------------------
85
86bool wxGetEnv(const wxString& var, wxString *value)
87{
88    // wxGetenv is defined as getenv()
89    wxChar *p = wxGetenv(var);
90    if ( !p )
91        return false;
92
93    if ( value )
94        *value = p;
95
96    return true;
97}
98
99bool wxSetEnv(const wxString& variable, const wxChar *value)
100{
101    wxString s = variable;
102    if ( value )
103        s << _T('=') << value;
104
105    // transform to ANSI
106    const char *p = s.mb_str();
107
108    // the string will be free()d by libc
109    char *buf = (char *)malloc(strlen(p) + 1);
110    strcpy(buf, p);
111
112    return putenv(buf) == 0;
113}
114
115//----------------------------------------------------------------------------
116// Hostname, username, home directory
117//----------------------------------------------------------------------------
118
119// Based on the MSW implementation
120//
121// Respects the following environment variables in this order: %HomeDrive% +
122// %HomePath%, %UserProfile%, $HOME. Otherwise takes program's directory if
123// wxApp has been initialised, otherwise returns ".".
124//
125const wxChar* wxGetHomeDir(wxString *home)
126{
127    wxString& strDir = *home;
128
129    strDir.clear();
130
131    // try HOMEDRIVE/PATH
132    const wxChar *szHome = wxGetenv(wxT("HOMEDRIVE"));
133    if ( szHome != NULL )
134        strDir << szHome;
135    szHome = wxGetenv(wxT("HOMEPATH"));
136
137    if ( szHome != NULL )
138    {
139        strDir << szHome;
140
141        // the idea is that under NT these variables have default values of
142        // "%systemdrive%:" and "\\". As we don't want to create our config
143        // files in the root directory of the system drive, we will create it
144        // in our program's dir. However, if the user took care to set
145        // HOMEPATH to something other than "\\", we suppose that he knows
146        // what he is doing and use the supplied value.
147        if ( wxStrcmp(szHome, wxT("\\")) == 0 )
148            strDir.clear();
149    }
150
151    if ( strDir.empty() )
152    {
153        // If we have a valid USERPROFILE directory, as is the case in
154        // Windows NT, 2000 and XP, we should use that as our home directory.
155        szHome = wxGetenv(wxT("USERPROFILE"));
156
157        if ( szHome != NULL )
158            strDir = szHome;
159    }
160
161    if ( strDir.empty() )
162    {
163        // If we have a valid HOME directory, as is used on many machines
164        // that have unix utilities on them, we should use that.
165        szHome = wxGetenv(wxT("HOME"));
166
167        if ( szHome != NULL )
168        {
169            strDir = szHome;
170            // when msys sets %HOME% it uses '/' (cygwin uses '\\')
171            strDir.Replace(_T("/"), _T("\\"));
172        }
173    }
174
175    if ( !strDir.empty() )
176    {
177        // sometimes the value of HOME may be "%USERPROFILE%", so reexpand the
178        // value once again, it shouldn't hurt anyhow
179        strDir = wxExpandEnvVars(strDir);
180    }
181    else // fall back to the program directory
182    {
183        if ( wxTheApp )
184        {
185            wxString prog(wxTheApp->argv[0]);
186#ifdef __DJGPP__
187            // djgpp startup code switches the slashes around, so restore them
188            prog.Replace(_T("/"), _T("\\"));
189#endif
190            // it needs to be a full path to be usable
191            if ( prog.compare(1, 2, _T(":\\")) == 0 )
192                wxSplitPath(prog, &strDir, NULL, NULL);
193        }
194        if ( strDir.empty() )
195        {
196            strDir = _T(".");
197        }
198    }
199
200    return strDir.c_str();
201}
202
203wxChar *wxGetUserHome(const wxString& user)
204{
205    static wxString home;
206
207    if (user.empty() || user == wxGetUserId())
208        return wx_const_cast(wxChar*, wxGetHomeDir(&home));
209    else
210        return _T("");
211}
212
213// returns %UserName%, $USER or just "user"
214//
215bool wxGetUserId(wxChar *buf, int n)
216{
217    const wxChar *user = wxGetenv(_T("UserName"));
218
219    if (!user)
220        user = wxGetenv(_T("USER"));
221
222    if (!user)
223        user = _T("user");
224
225    wxStrncpy(buf, user, n);
226    return true;
227}
228
229bool wxGetUserName(wxChar *buf, int n)
230{
231    return wxGetUserId(buf, n);
232}
233
234// returns %ComputerName%, or $HOSTNAME, or "host"
235//
236bool wxGetHostName(wxChar *buf, int n)
237{
238    const wxChar *host = wxGetenv(_T("ComputerName"));
239
240    if (!host)
241        host = wxGetenv(_T("HOSTNAME"));
242
243    if (!host)
244        host = _T("host");
245
246    wxStrncpy(buf, host, n);
247    return true;
248}
249
250// adds %UserDnsDomain% to wxGetHostName()
251//
252bool wxGetFullHostName(wxChar *buf, int n)
253{
254    wxGetHostName(buf, n);
255
256    const wxChar *domain = wxGetenv(_T("UserDnsDomain"));
257
258    if (domain)
259        wxStrncat(wxStrncat(buf, _T("."), n), domain, n);
260
261    return true;
262}
263
264//----------------------------------------------------------------------------
265// Processes
266//----------------------------------------------------------------------------
267
268unsigned long wxGetProcessId()
269{
270    return (unsigned long)getpid();
271}
272
273int wxKill(long pid, wxSignal sig, wxKillError *rc, int WXUNUSED(flags))
274{
275    int result = -1;
276
277    if (pid != (long)wxGetProcessId())
278    {
279        result = raise(sig);
280        if (rc)
281            *rc = result == 0 ? wxKILL_OK : wxKILL_BAD_SIGNAL;
282    }
283    else
284    {
285        wxLogDebug(_T("wxKill can only send signals to the current process under MSDOS"));
286        if (rc)
287            *rc = wxKILL_NO_PROCESS;
288    }
289
290    return result;
291}
292
293bool wxShell(const wxString& command /*=wxEmptyString*/)
294{
295    // FIXME: suspend/resume gui
296    int result = system(command);
297
298    if (result == -1)
299        wxLogSysError(_("can't execute '%s'"), command.c_str());
300
301    return result == 0;
302}
303
304long wxExecute(const wxString& command, int flags, wxProcess *process)
305{
306    // FIXME: shouldn't depend on wxCmdLineParser
307    wxArrayString args(wxCmdLineParser::ConvertStringToArgs(command));
308    size_t n = args.size();
309    wxChar **argv = new wxChar*[n + 1];
310
311    argv[n] = NULL;
312    while (n-- > 0)
313        argv[n] = wx_const_cast(wxChar*, args[n].c_str());
314
315    long result = wxExecute(argv, flags, process);
316
317    delete [] argv;
318    return result;
319}
320
321#if wxUSE_STREAMS
322
323// A wxFFileInputStream that deletes the file in it's destructor
324//
325class wxTempFileInStream : public wxFFileInputStream
326{
327public:
328    wxTempFileInStream(const wxString& name)
329        : wxFFileInputStream(name, _T("rt"))
330    { }
331
332    virtual ~wxTempFileInStream()
333    {
334        m_file->Close();
335        wxRemoveFile(m_file->GetName());
336    }
337};
338
339// A file descriptor that can be redirected to a file
340//
341class wxRedirectableFd
342{
343public:
344    wxRedirectableFd(int fd) : m_fd(fd), m_dup(-1) { }
345    ~wxRedirectableFd();
346
347    // Redirect the descriptor to a file, similar to ANSI C's freopen, but
348    // for low level descriptors. The desctructor un-redirects. If O_CREAT
349    // is in the flags then the destructor will delete the file unless it is
350    // given away with Release().
351    bool Reopen(const wxString& name, int flags);
352
353    // un-redirect the redirected file descriptor, closing the file, and give
354    // away the filename without deleting it
355    wxString Release();
356
357private:
358    // un-redirect the descriptor, closing the file
359    void Restore();
360
361    int m_fd;
362    int m_dup;
363    wxString m_name;
364};
365
366wxRedirectableFd::~wxRedirectableFd()
367{
368    Restore();
369    if (!m_name.empty())
370        wxRemoveFile(m_name);
371}
372
373bool wxRedirectableFd::Reopen(const wxString& name, int flags)
374{
375    wxASSERT(m_dup == -1);
376    bool result = false;
377
378    // save a duplicate so that the descriptor can be closed now and
379    // restored later
380    m_dup = dup(m_fd);
381
382    if (m_dup != -1)
383    {
384        int tmp = open(name.mb_str(), flags);
385
386        if (tmp != -1)
387        {
388            close(m_fd);
389
390            if (flags & O_CREAT)
391                m_name = name;
392
393            result = dup2(tmp, m_fd) == m_fd;
394            close(tmp);
395        }
396    }
397
398    if (!result)
399        wxLogSysError(_("error opening '%s'"), name.c_str());
400
401    return result;
402}
403
404void wxRedirectableFd::Restore()
405{
406    if (m_dup != -1)
407    {
408        close(m_fd);
409        dup2(m_dup, m_fd);
410        close(m_dup);
411        m_dup = -1;
412    }
413}
414
415wxString wxRedirectableFd::Release()
416{
417    Restore();
418    wxString name = m_name;
419    m_name.clear();
420    return name;
421}
422
423#endif // wxUSE_STREAMS
424
425// wxExecute implementation
426//
427long wxExecute(wxChar **argv, int flags, wxProcess *process)
428{
429#if wxUSE_STREAMS
430    const int STDIN = 0;
431    const int STDOUT = 1;
432    const int STDERR = 2;
433
434    wxRedirectableFd in(STDIN), out(STDOUT), err(STDERR);
435    bool redirect = process && process->IsRedirected() && (flags & wxEXEC_SYNC);
436
437    if (redirect)
438    {
439        // close stdin/out/err and reopen them as files
440        if (!in.Reopen(_T("NUL"), O_RDONLY | O_TEXT))
441            return -1;
442
443        if (!out.Reopen(wxFileName::CreateTempFileName(_T("out")),
444                        O_CREAT | O_WRONLY | O_TRUNC | O_TEXT))
445            return -1;
446
447        if (!err.Reopen(wxFileName::CreateTempFileName(_T("err")),
448                        O_CREAT | O_WRONLY | O_TRUNC | O_TEXT))
449            return -1;
450    }
451#endif // wxUSE_STREAMS
452
453    // FIXME: suspend/resume gui
454    int mode = flags & wxEXEC_SYNC ? P_WAIT : P_NOWAIT;
455    int result = spawnvp(mode, argv[0], argv);
456
457    if (result == -1)
458        wxLogSysError(_("can't execute '%s'"), argv[0]);
459
460#if wxUSE_STREAMS
461    if (redirect)
462        process->SetPipeStreams(new wxTempFileInStream(out.Release()),
463                                new wxFFileOutputStream(_T("NUL"), _T("wt")),
464                                new wxTempFileInStream(err.Release()));
465#endif // wxUSE_STREAMS
466
467    return result;
468}
469
470
471//----------------------------------------------------------------------------
472// OS-related
473//----------------------------------------------------------------------------
474
475wxString wxGetOsDescription()
476{
477    wxString osname(_T("DOS"));
478    return osname;
479}
480
481wxOperatingSystemId wxGetOsVersion(int *verMaj, int *verMin)
482{
483    if ( verMaj )
484        *verMaj = _osmajor;
485    if ( verMin )
486        *verMin = _osminor;
487
488    return wxOS_DOS;
489}
490
491bool wxIsPlatform64Bit()
492{
493    return false;
494}
495
496