inline wchar_t *wxStrncpy(wchar_t *dest, const char *src, size_t n)
     { return wxCRT_StrncpyW(dest, wxConvLibc.cMB2WC(src), n); }
 
+// this is a new function so we don't care about backwards compatibility and
+// so don't need to support wchar_t/char overloads
+inline size_t wxStrlcpy(char *dest, const char *src, size_t n)
+{
+    const size_t len = wxCRT_StrlenA(src);
+
+    if ( n )
+    {
+        if ( n-- > len )
+            n = len;
+        wxCRT_StrncpyA(dest, src, n);
+        dest[n] = '\0';
+    }
+
+    return len;
+}
+inline size_t wxStrlcpy(wchar_t *dest, const wchar_t *src, size_t n)
+{
+    const size_t len = wxCRT_StrlenW(src);
+    if ( n )
+    {
+        if ( n-- > len )
+            n = len;
+        wxCRT_StrncpyW(dest, src, n);
+        dest[n] = L'\0';
+    }
+
+    return len;
+}
+
 inline char *wxStrcat(char *dest, const char *src)
     { return wxCRT_StrcatA(dest, src); }
 inline wchar_t *wxStrcat(wchar_t *dest, const wchar_t *src)
 
                                const wxString& delims = wxDEFAULT_DELIMITERS,
                                wxStringTokenizerMode mode = wxTOKEN_DEFAULT);
 
+/**
+    Safe and more convenient replacement for strncpy().
+
+    This function copies the source string @a src to the destination buffer @a
+    dst of size @a n without overflowing the buffer and ensuring that it is
+    always @NUL-terminated.
+
+    Example of use:
+    @code
+        char buf[256];
+        if ( wxStrlcpy(buf, GetSomeString(), WXSIZEOF(buf)) > WXSIZEOF(buf) )
+            ... handle truncation ...
+    @endcode
+    Notice that using wxStrncpy() in similar way is wrong, the above is broadly
+    equivalent to
+    @code
+        char buf[256];
+        buf[WXSIZEOF(buf) - 1] = '\0';
+        wxStrncpy(buf, GetSomeString(), WXSIZEOF(buf) - 1);
+        if ( buf[WXSIZEOF(buf) - 1] != '\0' )
+        {
+            ... truncation occurred ...
+            // need to NUL-terminate string manually
+            buf[WXSIZEOF(buf) - 1] = '\0';
+        }
+    @endcode
+    which should explain the advantage of using wxStrlcpy().
+
+    Notice that this function is similar to the OpenBSD strlcpy() function.
+
+    The template parameter @a T can be either @c char or @c wchar_t.
+
+    @param dst
+        Destination buffer of size (greater or) equal to @a n.
+    @param src
+        @NUL-terminated source string.
+    @param n
+        The size of the destination buffer.
+    @return
+        The length of @a src, if the returned value is greater or equal to @a n
+        then there was not enough space in the destination buffer and the
+        string was truncated.
+
+    @since{2.9.0}
+
+    @header{wx/wxcrt.h}
+ */
+template <typename T>
+size_t wxStrlcpy(T *dst, const T *src, size_t n);
+
 /**
     This function replaces the dangerous standard function @e sprintf() and is
     like @e snprintf() available on some platforms. The only difference with
 
     static char buf[128];
 
     wxString fmt(dt->Format("%Y-%m-%d (%a) %H:%M:%S"));
-    wxStrncpy(buf, fmt + " (" + dt->GetValue().ToString() + " ticks)",
+    wxStrlcpy(buf, fmt + " (" + dt->GetValue().ToString() + " ticks)",
               WXSIZEOF(buf));
 
     return buf;
 
 #if !defined(__SMARTPHONE__) /* of WinCE */
     if( lpMsgBuf != 0 )
     {
-        wxStrncpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf) - 1);
-        s_szBuf[WXSIZEOF(s_szBuf) - 1] = wxS('\0');
+        wxStrlcpy(s_szBuf, (const wxChar *)lpMsgBuf, WXSIZEOF(s_szBuf));
 
         LocalFree(lpMsgBuf);
 
 
     if ( !email )
         return false;
 
-    wxStrncpy(address, email, maxSize - 1);
-    address[maxSize - 1] = wxT('\0');
+    wxStrlcpy(address, email, maxSize);
 
     return true;
 }
 
 bool wxGetClipboardFormatName(const wxDataFormat& dataFormat, char *formatName,
                               int maxCount)
 {
-    wxStrncpy( formatName, dataFormat.GetId().c_str(), maxCount );
+    wxStrlcpy( formatName, dataFormat.GetId().c_str(), maxCount );
 
     return true;
 }
 
     if (!user)
         user = _T("user");
 
-    wxStrncpy(buf, user, n);
+    wxStrlcpy(buf, user, n);
     return true;
 }
 
     if (!host)
         host = _T("host");
 
-    wxStrncpy(buf, host, n);
+    wxStrlcpy(buf, host, n);
     return true;
 }
 
 
                                       WNDCLASS *lpWndClass)
 {
     const size_t length = uniqueClassName.length() + 1; // for trailing NUL
-    wxChar *newChars = new wxChar[length];
-    wxStrncpy(newChars, uniqueClassName, length);
+    wxChar * const newChars = new wxChar[length];
+    wxStrlcpy(newChars, uniqueClassName, length);
     *className = newChars;
     lpWndClass->lpszClassName = *className;
 
 
 /* static */
 void wxCrashReport::SetFileName(const wxString& filename)
 {
-    wxStrncpy(gs_reportFilename, filename.c_str(), WXSIZEOF(gs_reportFilename) - 1);
-    gs_reportFilename[WXSIZEOF(gs_reportFilename) - 1] = _T('\0');
+    wxStrlcpy(gs_reportFilename, filename.wx_str(), WXSIZEOF(gs_reportFilename));
 }
 
 /* static */
 
 
     RASDIALPARAMS rasDialParams;
     rasDialParams.dwSize = sizeof(rasDialParams);
-    wxStrncpy(rasDialParams.szEntryName, entryName, RAS_MaxEntryName);
+    wxStrlcpy(rasDialParams.szEntryName, entryName, RAS_MaxEntryName);
 
     // do we have the username and password?
     if ( !username || !password )
     }
     else
     {
-        wxStrncpy(rasDialParams.szUserName, username, UNLEN);
-        wxStrncpy(rasDialParams.szPassword, password, PWLEN);
+        wxStrlcpy(rasDialParams.szUserName, username, UNLEN);
+        wxStrlcpy(rasDialParams.szPassword, password, PWLEN);
     }
 
     // default values for other fields
 
 
     //=== Setting defaultFileName >>=========================================
 
-    wxStrncpy(fileNameBuffer, m_fileName, wxMAXPATH-1);
-    fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
+    wxStrlcpy(fileNameBuffer, m_fileName, WXSIZEOF(fileNameBuffer));
 
     of.lpstrFile = fileNameBuffer;  // holds returned filename
     of.nMaxFile  = wxMAXPATH;
                 extension = extension + wxStrlen( extension ) + 1;
 
             m_fileName = AppendExtension(fileNameBuffer, extension);
-            wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.length(), wxMAXPATH-1));
-            fileNameBuffer[wxMin(m_fileName.length(), wxMAXPATH-1)] = wxT('\0');
+            wxStrlcpy(fileNameBuffer, m_fileName.c_str(), WXSIZEOF(fileNameBuffer));
         }
 
         m_path = fileNameBuffer;
 
 
 bool wxNativeFontInfo::SetFaceName(const wxString& facename)
 {
-    size_t len = WXSIZEOF(lf.lfFaceName);
-    wxStrncpy(lf.lfFaceName, facename, len);
-    lf.lfFaceName[len - 1] = '\0';    // truncate the face name
+    wxStrlcpy(lf.lfFaceName, facename, WXSIZEOF(lf.lfFaceName));
     return true;
 }
 
 
 #else // __WIN32__
     LOGFONT lf;
     lf.lfCharSet = (BYTE)m_charset;
-    wxStrncpy(lf.lfFaceName, m_facename, WXSIZEOF(lf.lfFaceName));
+    wxStrlcpy(lf.lfFaceName, m_facename, WXSIZEOF(lf.lfFaceName));
     lf.lfPitchAndFamily = 0;
     ::EnumFontFamiliesEx(hDC, &lf, (wxFONTENUMPROC)wxFontEnumeratorProc,
                          (LPARAM)this, 0 /* reserved */) ;
 
     wxZeroMemory(lf);       // all default values
 
     lf.lfCharSet = (BYTE)info.charset;
-    wxStrncpy(lf.lfFaceName, info.facename, WXSIZEOF(lf.lfFaceName));
+    wxStrlcpy(lf.lfFaceName, info.facename, WXSIZEOF(lf.lfFaceName));
 
     HFONT hfont = ::CreateFontIndirect(&lf);
     if ( !hfont )
 
                     if ( lvi.mask & LVIF_TEXT )
                     {
                         wxString text = OnGetItemText(item, lvi.iSubItem);
-                        wxStrncpy(lvi.pszText, text, lvi.cchTextMax - 1);
-                        lvi.pszText[lvi.cchTextMax - 1] = _T('\0');
+                        wxStrlcpy(lvi.pszText, text, lvi.cchTextMax);
                     }
 
                     // see comment at the end of wxListCtrl::GetColumn()
 
         {
             // NB: the cast is needed in the ANSI build, strangely enough
             //     dmDeviceName is BYTE[] and not char[] there
-            wxStrncpy(reinterpret_cast<wxChar *>(devMode->dmDeviceName),
+            wxStrlcpy(reinterpret_cast<wxChar *>(devMode->dmDeviceName),
                       name.wx_str(),
-                      WXSIZEOF(devMode->dmDeviceName) - 1);
-            devMode->dmDeviceName[WXSIZEOF(devMode->dmDeviceName) - 1] = wxT('\0');
+                      WXSIZEOF(devMode->dmDeviceName));
         }
 
         //// Colour
 
     notifyData.uFlags |= NIF_TIP;
     if ( !tooltip.empty() )
     {
-        wxStrncpy(notifyData.szTip, tooltip.wx_str(), WXSIZEOF(notifyData.szTip));
+        wxStrlcpy(notifyData.szTip, tooltip.wx_str(), WXSIZEOF(notifyData.szTip));
     }
 
     bool ok = wxShellNotifyIcon(m_iconAdded ? NIM_MODIFY
     notifyData = NotifyIconData(hwnd);
     notifyData.uFlags |= NIF_INFO;
     notifyData.uTimeout = msec;
-    wxStrncpy(notifyData.szInfo, text.wx_str(), WXSIZEOF(notifyData.szInfo));
-    wxStrncpy(notifyData.szInfoTitle, title.wx_str(),
+    wxStrlcpy(notifyData.szInfo, text.wx_str(), WXSIZEOF(notifyData.szInfo));
+    wxStrlcpy(notifyData.szInfoTitle, title.wx_str(),
                 WXSIZEOF(notifyData.szInfoTitle));
 
     if ( flags & wxICON_INFORMATION )
 
         cf.yHeight = 20*font.GetPointSize(); // 1 pt = 20 twips
         cf.bCharSet = lf.lfCharSet;
         cf.bPitchAndFamily = lf.lfPitchAndFamily;
-        wxStrncpy( cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName) );
+        wxStrlcpy(cf.szFaceName, lf.lfFaceName, WXSIZEOF(cf.szFaceName));
 
         // also deal with underline/italic/bold attributes: note that we must
         // always set CFM_ITALIC &c bits in dwMask, even if we don't set the
 
 #if defined(__WXWINCE__)
     // TODO-CE
     return false;
-#elif defined(__WIN32__) && !defined(__WXMICROWIN__)
+#else
     DWORD nSize = maxSize;
     if ( !::GetComputerName(buf, &nSize) )
     {
     }
 
     return true;
-#else
-    wxChar *sysname;
-    const wxChar *default_host = wxT("noname");
-
-    if ((sysname = wxGetenv(wxT("SYSTEM_NAME"))) == NULL) {
-        GetProfileString(WX_SECTION, eHOSTNAME, default_host, buf, maxSize - 1);
-    } else
-        wxStrncpy(buf, sysname, maxSize - 1);
-    buf[maxSize] = wxT('\0');
-    return *buf ? true : false;
 #endif
 }
 
 
             if ( !host.empty() )
             {
-                wxStrncpy(buf, host, maxSize);
+                wxStrlcpy(buf, host, maxSize);
 
                 return true;
             }
 #if defined(__WXWINCE__)
     // TODO-CE
     return false;
-#elif defined(__WIN32__) && !defined(__WXMICROWIN__)
+#else
     DWORD nSize = maxSize;
     if ( ::GetUserName(buf, &nSize) == 0 )
     {
     }
 
     return true;
-#else   // __WXMICROWIN__
-    wxChar *user;
-    const wxChar *default_id = wxT("anonymous");
-
-    // Can't assume we have NIS (PC-NFS) or some other ID daemon
-    // So we ...
-    if ( (user = wxGetenv(wxT("USER"))) == NULL &&
-         (user = wxGetenv(wxT("LOGNAME"))) == NULL )
-    {
-        // Use wxWidgets configuration data (comming soon)
-        GetProfileString(WX_SECTION, eUSERID, default_id, buf, maxSize - 1);
-    }
-    else
-    {
-        wxStrncpy(buf, user, maxSize - 1);
-    }
-
-    return *buf ? true : false;
 #endif
 }
 
     wxString name;
     if(!key.QueryValue(wxT("Owner"),name))
         return false;
-    wxStrncpy(buf, name.c_str(), maxSize-1);
-    buf[maxSize-1] = _T('\0');
+    wxStrlcpy(buf, name.c_str(), maxSize);
     return true;
 #elif defined(USE_NET_API)
     CHAR szUserName[256];
 
     if ( !ok )
     {
-        wxStrncpy(buf, wxT("Unknown User"), maxSize);
+        wxStrlcpy(buf, wxT("Unknown User"), maxSize);
     }
 
     return true;
 
 
     // Not all compilers put a zero at the end of the resource (e.g. BC++ doesn't).
     // so we need to find the length of the resource.
-    int len = ::SizeofResource(wxGetInstance(), hResource);
-    wxChar  *s = new wxChar[len+1];
-    wxStrncpy(s,theText,len);
-    s[len]=0;
+    int len = ::SizeofResource(wxGetInstance(), hResource) + 1;
+    wxChar *s = new wxChar[len];
+    wxStrlcpy(s, theText, len);
 
     // Obsolete in WIN32
 #ifndef __WIN32__
 
         // if we got TTN_NEEDTEXTW in Unicode build: in this case we just have
         // to copy the string we have into the buffer
         static wxChar buf[513];
-        wxStrncpy(buf, ttip.c_str(), WXSIZEOF(buf) - 1);
-        buf[WXSIZEOF(buf) - 1] = _T('\0');
+        wxStrlcpy(buf, ttip.c_str(), WXSIZEOF(buf));
         ttText->lpszText = buf;
     }
 
 
   const wxString&                   sFacename
 )
 {
-    wxStrncpy((wxChar*)fa.szFacename, sFacename, WXSIZEOF(fa.szFacename));
+    wxStrlcpy((wxChar*)fa.szFacename, sFacename, WXSIZEOF(fa.szFacename));
     return true;
 } // end of wxNativeFontInfo::SetFaceName
 
 
 #ifdef __WIN32__
     LOGFONT lf;
     lf.lfCharSet = m_charset;
-    wxStrncpy(lf.lfFaceName, m_facename, WXSIZEOF(lf.lfFaceName));
+    wxStrlcpy(lf.lfFaceName, m_facename, WXSIZEOF(lf.lfFaceName));
     lf.lfPitchAndFamily = 0;
     ::EnumFontFamiliesEx(hDC, &lf, (wxFONTENUMPROC)wxFontEnumeratorProc,
                          (LPARAM)this, 0) ;
 
     vLogFont.fsFontUse = FATTR_FONTUSE_OUTLINE |      // only outline fonts allowed
                          FATTR_FONTUSE_TRANSFORMABLE; // may be transformed
 
-    wxStrncpy((wxChar*)vLogFont.szFacename, rInfo.facename.c_str(), WXSIZEOF(vLogFont.szFacename));
+    wxStrlcpy((wxChar*)vLogFont.szFacename, rInfo.facename.c_str(), WXSIZEOF(vLogFont.szFacename));
 
     if (!::GpiCreateLogFont( hPS
                             ,NULL
             break;
     }
 
-    wxStrncpy(zFontFaceName, sFaceName.c_str(), WXSIZEOF(zFontFaceName));
+    wxStrlcpy(zFontFaceName, sFaceName.c_str(), WXSIZEOF(zFontFaceName));
     nPointSize = pFont->GetPointSize();
 
     //
 
                                 ,(void*)zBuf
                                 ,(ULONG)nMaxSize - 1
                                );
+        zBuf[nMaxSize] = _T('\0');
     }
     else
     {
-        wxStrncpy(zBuf, zSysname, nMaxSize - 1);
+        wxStrlcpy(zBuf, zSysname, nMaxSize);
     }
-
-    zBuf[nMaxSize] = _T('\0');
 #endif
 
     return *zBuf ? true : false;
 #ifdef USE_NET_API
     wxGetUserId( zBuf, nMaxSize );
 #else
-    wxStrncpy(zBuf, _T("Unknown User"), nMaxSize);
+    wxStrlcpy(zBuf, _T("Unknown User"), nMaxSize);
 #endif
     return true;
 }
 
         return false;
     }
 
-    wxStrncpy (buf, wxSafeConvertMB2WX(id), maxSize - 1);
+    wxStrlcpy(buf, wxSafeConvertMB2WX(id), maxSize);
 
     // free the buffer
     MemPtrUnlock(id);
 
     bool ok = uname(&uts) != -1;
     if ( ok )
     {
-        wxStrncpy(buf, wxSafeConvertMB2WX(uts.nodename), sz - 1);
-        buf[sz] = wxT('\0');
+        wxStrlcpy(buf, wxSafeConvertMB2WX(uts.nodename), sz);
     }
 #elif defined(HAVE_GETHOSTNAME)
     char cbuf[sz];
     bool ok = gethostname(cbuf, sz) != -1;
     if ( ok )
     {
-        wxStrncpy(buf, wxSafeConvertMB2WX(cbuf), sz - 1);
-        buf[sz] = wxT('\0');
+        wxStrlcpy(buf, wxSafeConvertMB2WX(cbuf), sz);
     }
 #else // no uname, no gethostname
     wxFAIL_MSG(wxT("don't know host name for this machine"));
             else
             {
                 // the canonical name
-                wxStrncpy(buf, wxSafeConvertMB2WX(host->h_name), sz);
+                wxStrlcpy(buf, wxSafeConvertMB2WX(host->h_name), sz);
             }
         }
         //else: it's already a FQDN (BSD behaves this way)
     *buf = wxT('\0');
     if ((who = getpwuid(getuid ())) != NULL)
     {
-        wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_name), sz - 1);
+        wxStrlcpy (buf, wxSafeConvertMB2WX(who->pw_name), sz);
         return true;
     }
 
        char *comma = strchr(who->pw_gecos, ',');
        if (comma)
            *comma = '\0'; // cut off non-name comment fields
-       wxStrncpy (buf, wxSafeConvertMB2WX(who->pw_gecos), sz - 1);
+       wxStrlcpy(buf, wxSafeConvertMB2WX(who->pw_gecos), sz);
        return true;
     }