/* (C) 2003-2007 Willem Jan Hengeveld <itsme@xs4all.nl>
 * Web: http://www.xs4all.nl/~itsme/
 *      http://wiki.xda-developers.com/
 *
 * $Id$
 */

// done: fix bug when copying from dev to a dir ending in slash:
//      pget -f "/*" tmp/
// -> incorrect error: source is wildcard to multiple files: target must be an existing directory
//
//
// todo: * support unicode filenames
//            -> need to create win32 file function lib which supports both 'A' and 'W' calls, to be able to stay backwd compatible with w9x
//       * support %CE1%  etc path indicators
//         .... names are somewhere in shellres.dll 
//         PARTIALLY DONE: now have csidl paths
//
//       "pput -c filename"  should return an error when the file exists.
//
//       pget \path\wild*     should return an error when the file exists,
//                          ( and fails to copy because it does not overwrite )
//
// todo: preserve file modification time
// done: add '-f' option, like "tail -f", outputting all file updates
// todo: add '-o' + '-l' options, to specify a specific section of the file
//           and '-s' to seek the input file
//
// todo: improve wildcard/recursion resolving -> make one function with a callback, to handle all in one place
//
/*
csidl values
  http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/reference/enums/csidl.asp
%ce% values:
  http://msdn.microsoft.com/library/en-us/dv_evtuv/html/etgrfwindowscedirectoryidentifiers.asp
CEShell_77  : get %CE% path
CEShell_76  : get csidl path
see also SHGetSpecialFolderLocation, SHGetSpecialFolderPath

CeGetSpecialFolderPath

%CE% csidl path                                        shellres string
00
12
           "\"                                         0x2267
06         "\Program Files\Accessories"                0x2281
07         "\Program Files\Communication"              0x2282
08         "\Program Files\Games"                      0x2283
0a         "\Program Files\Office"                     0x2285
09         "\Program Files\Pocket Outlook"             0x2284
10         "\Windows\Recent"                           0x2262
11         "\Windows\Start Menu"                       0x2266
0b         "\Windows\Start Menu"                       0x228a
0c         "\Windows\Start Menu\Accessories"           0x2286
0d         "\Windows\Start Menu\Communication"         0x2287
0e         "\Windows\Start Menu\Games"                 0x2288
     00     {00021400-0000-0000-C000-000000000046}            CSIDL_DESKTOP
03   00    "\My Documents"                             0x2260
     01 CSIDL_INTERNET                   // Internet Explorer (icon on desktop)
0b   02    "\Windows\Start Menu\Programs"              0x2263 CSIDL_PROGRAMS
     03     {000314fe-0000-0000-C000-000000000046}            CSIDL_CONTROLS - control panel
     04                                                       CSIDL_PRINTERS - My Computer\Printers
05   05    "\My Documents"                             0x2269 CSIDL_PERSONAL
     06    "\Windows\Favorites"                        0x2264 CSIDL_FAVORITES
04   07    "\Windows\StartUp"                          0x2261 CSIDL_STARTUP
     08                                                       CSIDL_RECENT
     09                                                       CSIDL_SENDTO
     0a     {000214A1-0000-0000-C000-000000000046}            CSIDL_BITBUCKET - Recycle Bin
     0b    "\My Documents\My Music"                    0x2274
     0b CSIDL_STARTMENU                  // <user name>\Start Menu
     0c CSIDL_MYDOCUMENTS                // logical "My Documents" desktop icon
     0d CSIDL_MYMUSIC                    // "My Music" folder
     0e CSIDL_MYVIDEO                    // "My Videos" folder
     0f
     10 CSIDL_DESKTOPDIRECTORY           // <user name>\Desktop
     11     {000214A0-0000-0000-C000-000000000046}            CSIDL_DRIVES // My Computer
     12 CSIDL_NETWORK                    // Network Neighborhood (My Network Places)
     13 CSIDL_NETHOOD                    // <user name>\nethood
0f   14    "\Windows\Fonts"                            0x2265 CSIDL_FONTS
     15 CSIDL_TEMPLATES
     16 CSIDL_COMMON_STARTMENU           // All Users\Start Menu
     17 CSIDL_COMMON_PROGRAMS            // All Users\Start Menu\Programs
     18 CSIDL_COMMON_STARTUP             // All Users\Startup
     19 CSIDL_COMMON_DESKTOPDIRECTORY    // All Users\Desktop
13   1a    "\Application Data"                         0x2268 CSIDL_APPDATA
     1b CSIDL_PRINTHOOD                  // <user name>\PrintHood
     1c CSIDL_LOCAL_APPDATA              // non roaming, user\Local Settings\Application Data
     1d CSIDL_ALTSTARTUP                 // non localized startup
     1e CSIDL_COMMON_ALTSTARTUP          // non localized common startup
     1f CSIDL_COMMON_FAVORITES
     20 CSIDL_INTERNET_CACHE
     21 CSIDL_COOKIES
     22 CSIDL_HISTORY
     23 CSIDL_COMMON_APPDATA             // All Users\Application Data
02   24    "\Windows"                                  0x2280 CSIDL_WINDOWS
     25 CSIDL_SYSTEM                     // GetSystemDirectory()
01   26    "\Program Files"                            0x2289 CSIDL_PROGRAM_FILES
     27 CSIDL_MYPICTURES                 // C:\Program Files\My Pictures
     28 CSIDL_PROFILE                    // USERPROFILE
     29 CSIDL_SYSTEMX86                  // x86 system directory on RISC
     2a CSIDL_PROGRAM_FILESX86           // x86 C:\Program Files on RISC
     2b CSIDL_PROGRAM_FILES_COMMON       // C:\Program Files\Common
     2c CSIDL_PROGRAM_FILES_COMMONX86    // x86 Program Files\Common on RISC
     2d CSIDL_COMMON_TEMPLATES           // All Users\Templates
     2e CSIDL_COMMON_DOCUMENTS           // All Users\Documents
     2f CSIDL_COMMON_ADMINTOOLS          // All Users\Start Menu\Programs\Administrative Tools
     30 CSIDL_ADMINTOOLS                 // <user name>\Start Menu\Programs\Administrative Tools
     30 ssfPROGRAMFILESx86       Special Folder PROGRAM FILESx86
     31 CSIDL_CONNECTIONS                // Network and Dial-up Connections
     32
     33
     34
     35 CSIDL_COMMON_MUSIC               // All Users\My Music
     36 CSIDL_COMMON_PICTURES            // All Users\My Pictures
     37 CSIDL_COMMON_VIDEO               // All Users\My Video
     38 CSIDL_RESOURCES                  // %windir%\Resources\, For theme and other windows resources.
     39 CSIDL_RESOURCES_LOCALIZED        // %windir%\Resources\<LangID>, for theme and other windows specific resources.
     3a CSIDL_COMMON_OEM_LINKS           // Links to All Users OEM specific apps
     3b CSIDL_CDBURN_AREA                // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
     3c
     3d CSIDL_COMPUTERSNEARME            // Computers Near Me (computered from Workgroup membership)
     3e
     3f
8000 CSIDL_FLAG_CREATE                // combine with CSIDL_ value to force folder creation in SHGetFolderPath()
4000 CSIDL_FLAG_DONT_VERIFY           // combine with CSIDL_ value to return an unverified folder path
ff00 CSIDL_FLAG_MASK                  // mask for all possible flag values
1000 CSIDL_FLAG_NO_ALIAS              // combine with CSIDL_ value to insure non-alias versions of the pidl
0800 CSIDL_FLAG_PER_USER_INIT         // combine with CSIDL_ value to indicate per-user init (eg. upgrade)

*/
//
//
//       * when operating on 1 file, do write error message
#include <util/wintypes.h>
#include <stdio.h>
#include <string.h>
#include <util/rapitypes.h>
#include "debug.h"
#include "stringutils.h"
#include "vectorutils.h"
#include "FileFunctions.h"
#include "csidlpaths.h"

bool g_allowOverwrite= false;
bool g_monitorFiles= false;
bool g_useStdout= false;
bool g_bRecurse= false;
bool g_bSysrootRelative= false; // relative to \\ | \\Storage | \\IPSM

// 0 : no output
// 1 : output unexpected errors
// 2 : output all errors
// 3 : output copied file names
int verbose= 1;
int nr_of_src_files= 0;

bool isWin32Directory(const std::tstring& name)
{
    return GetFileInfo(name)==AT_ISDIRECTORY;
}
bool isWin32File(const std::tstring& name)
{
    return GetFileInfo(name)==AT_ISFILE;
}

int getCeAttributes(const std::tstring& name)
{
    DWORD dwAttr = CeGetFileAttributes( expand_csidl(ToWString(name)).c_str() );
    if (0xFFFFFFFF == dwAttr)
        return AT_NONEXISTANT;

    if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
        return AT_ISDIRECTORY;

    return AT_ISFILE;
}
bool isCeDirectory(const std::tstring& name)
{
    return getCeAttributes(name)==AT_ISDIRECTORY;
}
bool isCeFile(const std::tstring& name)
{
    return getCeAttributes(name)==AT_ISFILE;
}


bool hasWildCards(const std::tstring& name)
{
    return name.find_first_of(_T("*?"))!=name.npos;
}
std::tstring concat_path(const std::tstring& p1, const std::tstring& p2)
{
    std::tstring result(p1);

    if (result.size() && result[result.size()-1]!='/' && result[result.size()-1]!='\\')
        result += '\\';

    result += p2;

    return result;
}
std::tstring GetBasePath(const std::tstring& name)
{
    size_t lastslash= name.find_last_of(_T("\\/"));
    if (lastslash==name.npos)
        return _T("");

    return name.substr(0, lastslash);
}
std::tstring GetBaseName(const std::tstring& name)
{
    size_t lastslash= name.find_last_of(_T("\\/"));
    if (lastslash==name.npos)
        return name;

    return name.substr(lastslash+1);
}

bool Win32FileGlob(const std::tstring& name, TStringList& list)
{
    nr_of_src_files--;

    WIN32_FIND_DATA wfd;
    HANDLE hFind = FindFirstFile( name.c_str(), &wfd);
    if (INVALID_HANDLE_VALUE == hFind) {
        if (GetLastError()==ERROR_NO_MORE_FILES) {
            return true;
        }
        return false;
    }

    std::tstring rootpath= GetBasePath(name);
    do {
        if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            list.push_back(concat_path(rootpath, wfd.cFileName));
        }

    } while (FindNextFile(hFind, &wfd));

    FindClose(hFind);

    nr_of_src_files += list.size();;

    return true;
}
bool CeFileGlob(const std::tstring& name, TStringList& filelist, TStringList& dirlist)
{
    nr_of_src_files--;

    CE_FIND_DATA wfd;
    HANDLE hFind = CeFindFirstFile( expand_csidl(ToWString(name)).c_str(), &wfd);
    if (INVALID_HANDLE_VALUE == hFind) {
        if (CeGetLastError()==ERROR_NO_MORE_FILES)
            return true;
        return false;
    }

    std::tstring rootpath= GetBasePath(name);
    do {
        if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
            filelist.push_back(concat_path(rootpath, ToTString(wfd.cFileName)));
        }
        else if (ToWString(wfd.cFileName)!=L"." && ToWString(wfd.cFileName)!=L"..")
        {
            dirlist.push_back(concat_path(rootpath, ToTString(wfd.cFileName)));
        }

    } while (CeFindNextFile(hFind, &wfd));

    CeFindClose(hFind);

    nr_of_src_files += filelist.size();;

    return true;
}
bool CopyWin32FileToCeFile(const std::tstring& src, const std::tstring& dstfile)
{
    if (!g_allowOverwrite && isCeFile(dstfile))
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) debug("destination file already exists - specify -f to overwrite\n");
        return false;
    }
    HANDLE hSrc = CreateFile( src.c_str(), GENERIC_READ, FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hSrc)
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) error("%hs", ToString(src).c_str());
        return false;
    }

    HANDLE hDest = CeCreateFile( expand_csidl(ToWString(dstfile)).c_str(), GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hDest )
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) ceerror("WCE:%hs", ToString(dstfile).c_str());
        return false;
    }

    if (verbose>2) debug("Copying %hs to WCE:%hs\n", ToString(src).c_str(), ToString(dstfile).c_str());

    ByteVector buffer;  buffer.resize(65536);

    bool bRes= true;

    DWORD dwNumRead;
    do
    {
        if (!ReadFile( hSrc, vectorptr(buffer), buffer.size(), &dwNumRead, NULL))
        {
            if (verbose>0) error("reading %hs", ToString(src).c_str());
            bRes= false;
            break;
        }

        DWORD dwNumWritten;
        if (!CeWriteFile( hDest, vectorptr(buffer), dwNumRead, &dwNumWritten, NULL))
        {
            if (verbose>0) ceerror("Writing WCE:%hs", ToString(dstfile).c_str());
            bRes= false;
            break;
        }
    } while (dwNumRead);

    CeCloseHandle( hDest);
    CloseHandle (hSrc);

    return bRes;
}
bool CopyStdinToCeFile(const std::tstring& dstfile)
{
    if (!g_allowOverwrite && isCeFile(dstfile))
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) debug("destination file already exists - specify -f to overwrite\n");
        return false;
    }
    HANDLE hDest = CeCreateFile( expand_csidl(ToWString(dstfile)).c_str(), GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hDest )
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) ceerror("WCE:%hs", ToString(dstfile).c_str());
        return false;
    }

    if (verbose>2) debug("Copying STDIN to WCE:%hs\n", ToString(dstfile).c_str());

    ByteVector buffer;  buffer.resize(65536);

    bool bRes= true;

    DWORD dwNumRead;
    while (!feof(stdin)) {
        dwNumRead= fread(vectorptr(buffer), 1, buffer.size(), stdin);
        if (dwNumRead==0)
            break;

        DWORD dwNumWritten;
        if (!CeWriteFile( hDest, vectorptr(buffer), dwNumRead, &dwNumWritten, NULL))
        {
            if (verbose>0) ceerror("Writing WCE:%hs", ToString(dstfile).c_str());
            bRes= false;
            break;
        }
    }

    CeCloseHandle( hDest);

    return bRes;
}
bool CopyCeFileToWin32File(const std::tstring& src, const std::tstring& dstfile)
{
    if (!g_allowOverwrite && isWin32File(dstfile))
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) debug("destination file already exists - specify -f to overwrite\n");
        return false;
    }
    HANDLE hSrc = CeCreateFile( expand_csidl(ToWString(src)).c_str(), GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hSrc)
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) ceerror("WCE:%hs", ToString(src).c_str());
        return false;
    }

    HANDLE hDest = CreateFile( dstfile.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hDest )
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) error("%hs", ToString(dstfile).c_str());
        return false;
    }

    if (verbose>2) debug("Copying WCE:%hs to %hs\n", ToString(src).c_str(), ToString(dstfile).c_str());

    ByteVector buffer;  buffer.resize(65536);

    bool bRes= true;

    DWORD dwNumRead;
    do
    {
        if (!CeReadFile( hSrc, vectorptr(buffer), buffer.size(), &dwNumRead, NULL))
        {
            if (verbose>0) ceerror("reading WCE:%hs", ToString(src).c_str());
            bRes= false;
            break;
        }

        DWORD dwNumWritten;
        if (!WriteFile( hDest, vectorptr(buffer), dwNumRead, &dwNumWritten, NULL))
        {
            if (verbose>0) error("Writing %hs", ToString(dstfile).c_str());
            bRes= false;
            break;
        }
    } while (dwNumRead);

    CloseHandle( hDest);
    CeCloseHandle (hSrc);

    return bRes;
}
bool PrintCeFile(const std::tstring& src)
{
    HANDLE hSrc = CeCreateFile( expand_csidl(ToWString(src)).c_str(), GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (INVALID_HANDLE_VALUE == hSrc)
    {
        if ((nr_of_src_files==1 && verbose>0) || verbose>1) ceerror("WCE:%hs", ToString(src).c_str());
        return false;
    }

    if ((nr_of_src_files>1 && verbose>0) || verbose>1) debug("Printing WCE:%hs\n", ToString(src).c_str());

    ByteVector buffer;  buffer.resize(65536);

    bool bRes= true;

    DWORD dwNumRead;
    do
    {
        if (!CeReadFile( hSrc, vectorptr(buffer), buffer.size(), &dwNumRead, NULL))
        {
            if (verbose>0) ceerror("Reading WCE:%hs", ToString(src).c_str());
            bRes= false;
            break;
        }
        fwrite(vectorptr(buffer), 1, dwNumRead, stdout);
    } while (dwNumRead);

    CeCloseHandle (hSrc);

    printf("\n");

    return bRes;
}
int64_t ce_file_size(HANDLE h)
{
    DWORD high;
    DWORD low= CeGetFileSize(h, &high);
    if (low==INVALID_FILE_SIZE && GetLastError())
        return -1;

    return int64_t(low)|(int64_t(high)<<32);
}
int64_t ce_file_tell(HANDLE h)
{
    LONG high;
    DWORD low= CeSetFilePointer(h, 0, &high, FILE_CURRENT);
    if (low==0xFFFFFFFF && GetLastError())
        return -1;

    return int64_t(low)|(int64_t(high)<<32);
}
int64_t ce_file_seek(HANDLE h, int64_t pos, int wence)
{
    LONG high= LONG(pos>>32);
    DWORD low= CeSetFilePointer(h, DWORD(pos), &high, wence);
    if (low==0xFFFFFFFF && GetLastError())
        return -1;

    return int64_t(low)|(int64_t(high)<<32);
}
struct fileinfo {
    fileinfo(HANDLE h, const std::string& name, int64_t pos)
        : h(h), name(name), pos(pos)
    { }

    HANDLE h;
    std::string name;
    int64_t pos;
};

void PrintCeFilesChanges(TStringList args)
{
    typedef std::vector<fileinfo> fileinfo_list;
    fileinfo_list srcs;

    for (unsigned i= 0 ; i<args.size() ; i++)
    {
        if (hasWildCards(args[i])) {
            TStringList files, dirs;
            if (!CeFileGlob(args[i], files, dirs)) {
                if (verbose>0) error("WCE:%hs\n", ToString(args[i]).c_str());
            }
            else {
                for (unsigned j= 0 ; j<files.size() ; j++)
                    args.push_back(files[j]);
            }
            continue;
        }
        HANDLE h= CeCreateFile( expand_csidl(ToWString(args[i])).c_str(), GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ,
                NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

        if (h!=INVALID_HANDLE_VALUE) {
            int64_t size= ce_file_size(h);
            srcs.push_back(fileinfo(h, ToString(args[i]), ce_file_seek(h, size<1024 ? 0 : size-1024, FILE_CURRENT)));

            printf("monitoring %08x  %9lld %s\n", srcs.back().h, srcs.back().pos, srcs.back().name.c_str());
        }
        else {
            ceerror("WCE:%hs", ToString(args[i]).c_str());
        }
    }

    HANDLE lastheader= INVALID_HANDLE_VALUE;
    while(!srcs.empty()) {
        for (fileinfo_list::iterator i=srcs.begin() ; i!=srcs.end() ; i++)
        {
            int64_t size= ce_file_size(i->h);
            if (size > i->pos) {
                if (lastheader!=i->h) {
                    printf("\n==> %9lld %s\n", i->pos, i->name.c_str());
                    lastheader= i->h;
                }
                ByteVector buffer(size - i->pos);
                DWORD nRead;
                if (!CeReadFile(i->h, &buffer[0], buffer.size(), &nRead, 0)) {
                    printf("\n**** stop %08x %9lld %s\n", i->h, i->pos, i->name.c_str());
                    CeCloseHandle(i->h);
                    srcs.erase(i);
                    break;
                }
                fwrite(&buffer[0], 1, nRead, stdout);
                i->pos= size;
            }
            else if (size < i->pos) {
                printf("\n**** file shrank %08x %9lld %s\n", i->h, i->pos, i->name.c_str());
                lastheader= i->h;
            }
        }
        Sleep(1000);
    }
}
bool CopyWin32FileToCeDirectory(const std::tstring& src, const std::tstring& dstdir)
{
    size_t lastslash= src.find_last_of(_T("\\/"));
    if (lastslash==src.npos)
        return CopyWin32FileToCeFile(src, concat_path(dstdir, src));
    else
        return CopyWin32FileToCeFile(src, concat_path(dstdir, src.substr(lastslash+1)));
}
bool CopyCeFileToWin32Directory(const std::tstring& src, const std::tstring& dstdir)
{
    size_t lastslash= src.find_last_of(_T("\\/"));
    if (lastslash==src.npos)
        return CopyCeFileToWin32File(src, concat_path(dstdir, src));
    else                        
        return CopyCeFileToWin32File(src, concat_path(dstdir, src.substr(lastslash+1)));
}


bool CopyWin32ToCe(const TStringList& args, const std::tstring& dst)
{
    bool bRes= true;
    nr_of_src_files += args.size();
    if (isCeDirectory(dst)) {
        for (size_t i=0 ; i<args.size() ; i++) {
            std::tstring src= args[i];
            if (hasWildCards(src)) {
                TStringList files;
                if (!Win32FileGlob(src, files))
                {
                    if (verbose>0) error("%hs\n", ToString(src).c_str());
                    return false;
                }
                for (size_t j= 0 ; j<files.size() ; j++) {
                    bRes = (g_bRecurse||bRes) && CopyWin32FileToCeDirectory(files[j], dst);
                }
            }
            else {
                bRes = (g_bRecurse||bRes) && CopyWin32FileToCeDirectory(src, dst);
            }
        }
    }
    else {
        if (args.size()>1)
        {
            if (verbose>0) debug("multiple source files: target must be an existing directory\n");
            return false;
        }
        std::tstring src= args[0];
        if (hasWildCards(src))
        {
            TStringList files;
            if (!Win32FileGlob(src, files))
            {
                if (verbose>0) error("%hs\n", ToString(src).c_str());
                return false;
            }
            if (files.size()>1)
            {
                if (verbose>0) debug("source(%hs) is wildcard to multiple files: target(%hs) must be an existing directory\n",
                        ToString(src).c_str(), ToString(dst).c_str());
                return false;
            }
            src= files[0];
        }
        bRes = (g_bRecurse||bRes) && CopyWin32FileToCeFile(src, dst);
    }
    return bRes;
}

bool CopyCeToWin32(const TStringList& args, const std::tstring& dst)
{
    bool bRes= true;
    nr_of_src_files += args.size();
    if (dst == _T("-")) {
        if (g_monitorFiles) {
            PrintCeFilesChanges(args);
            return true;
        }
        // output all to stdout
        for (size_t i=0 ; i<args.size() ; i++) {
            std::tstring src= args[i];
            if (isCeDirectory(src))
                src= concat_path(src, _T("*"));
            if (hasWildCards(src)) {
                TStringList files, dirs;
                if (!CeFileGlob(src, files, dirs))
                {
                    if (verbose>0) error("WCE:%hs\n", ToString(src).c_str());
                    return false;
                }
                for (size_t j= 0 ; j<files.size() ; j++) {
                    PrintCeFile(files[j]);
                }
                if (g_bRecurse) {
                    for (size_t j= 0 ; j<dirs.size() ; j++) {
                        TStringList oneitem;
                        oneitem.push_back(concat_path(dirs[j], _T("*")));
                        bRes = (g_bRecurse||bRes) && CopyCeToWin32(oneitem, dst);
                    }
                }
            }
            else {
                bRes = (g_bRecurse||bRes) && PrintCeFile(src);
            }
        }
    }
    else if (isWin32Directory(dst)) {
        for (size_t i=0 ; i<args.size() ; i++) {
            std::tstring src= args[i];
            if (isCeDirectory(src))
                src= concat_path(src, _T("*"));
            if (hasWildCards(src)) {
                TStringList files, dirs;
                if (!CeFileGlob(src, files, dirs))
                {
                    if (verbose>0) error("WCE:%hs\n", ToString(src).c_str());
                    return false;
                }
                for (size_t j= 0 ; j<files.size() ; j++) {
                    bRes = (g_bRecurse||bRes) && CopyCeFileToWin32Directory(files[j], dst);
                }
                if (g_bRecurse) {
                    for (size_t j= 0 ; j<dirs.size() ; j++) {
                        TStringList oneitem;
                        oneitem.push_back(concat_path(dirs[j], _T("*")));
                        CreateDirectory(concat_path(dst, GetBaseName(dirs[j])).c_str(), NULL);
                        bRes = (g_bRecurse||bRes) && CopyCeToWin32(oneitem, concat_path(dst, GetBaseName(dirs[j])));
                    }
                }
            }
            else {
                bRes = (g_bRecurse||bRes) && CopyCeFileToWin32Directory(src, dst);
            }
        }
    }
    else {      // dst != "-" && !isWin32Directory(dst)
        if (args.size()>1)
        {
            if (verbose>0) debug("multiple source files: target must be an existing directory\n");
            return false;
        }
        std::tstring src= args[0];
        if (isCeDirectory(src))
            src= concat_path(src, _T("*"));
        if (hasWildCards(src))
        {
            TStringList files, dirs;
            if (!CeFileGlob(src, files, dirs))
            {
                if (verbose>0) error("WCE:%hs\n", ToString(src).c_str());
                return false;
            }
            if (files.size()>1)
            {
                if (verbose>0) debug("source is wildcard to multiple files: target must be an existing directory\n");
                return false;
            }
            src= files[0];
        }
        bRes = (g_bRecurse||bRes) && CopyCeFileToWin32File(src, dst);
    }
    return bRes;
}
void usage(const std::string& cmd)
{
    printf("(C) 2003-2008 Willem jan Hengeveld  itsme@xs4all.nl\n");
    if (cmd.substr(0,4)=="pput") {
        debug("Usage: pput [-f] <localfile(s)> <devicefile|path>\n");
        debug("     -c : write stdin to file\n");
    }
    else if (cmd.substr(0,4)=="pget") {
        debug("Usage: pget [-f] <deviefile(s)> <localfile|path>\n");
        debug("     -c : print files to stdout\n");
    }
    debug("     -f : overwrite existing files\n");
    debug("     -r : recursive copy\n");
    debug("     -q : less verbose output\n");
    debug("     -v : more verbose output\n");
    debug("     -l : list available special paths\n");
    debug("       source files may contain wildcards\n");
}
void removetralingslash(std::tstring& dst)
{
    while (dst[dst.size()-1]==_T('/') || dst[dst.size()-1]==_T('\\')) {
        dst.resize(dst.size()-1);
    }
}
int main(int argc, char *argv[])
{
    std::string cmd= tolower(ToString(GetBaseName(ToTString(argv[0]))));
    bool do_list_csidl_paths= false;

    DebugStdOut();

    TStringList args;

    for (int i=1 ; i<argc ; i++)
    {
        if (argv[i][0]=='-') switch(argv[i][1])
        {
            case 'f': g_allowOverwrite= true; break;
            case 'm': g_useStdout= true; g_monitorFiles= true; break;
            case 'r': g_bRecurse= true; break;
            case 'R': g_bSysrootRelative= true; break;
            case 'c': g_useStdout= true; break;
            case 'v': verbose++; break;
            case 'l': do_list_csidl_paths= true; break;
            case 'q': verbose--; break;
              // todo: add option to monitor a logfile for change, and output changes
            default:
                usage(cmd);
                return 1;
        }
        else {
            args.push_back(ToTString(argv[i]));
        }
    }

    if (FAILED(CeRapiInit()))
    {
        error("rapi init Failed");
        return 1;
    }
    bool bRes= true;

    if (do_list_csidl_paths)  {
        list_csidl_paths();
    }
    else if (cmd.substr(0,4)=="pput") {
        if (g_useStdout) {
            if (args.size()!=1)
            {
                usage(cmd);
                return 1;
            }
            bRes = (g_bRecurse||bRes) && CopyStdinToCeFile(ToTString(args.back()));
        }
        else if (args.size()<2)
        {
            usage(cmd);
            return 1;
        }
        else {
            std::tstring dst= args.back();  args.pop_back();

            bRes = (g_bRecurse||bRes) && CopyWin32ToCe(args, dst);
        }
    }
    else if (cmd.substr(0,4)=="pget") {
        std::tstring dst;
        if (args.size()==0)
        {
            usage(cmd);
            return 1;
        }
        else if (g_useStdout) {
            dst= _T("-");
        }
        else if (args.size()==1)
        {
            dst= _T(".");
        }
        else {
            dst= args.back();  args.pop_back();
            removetralingslash(dst);
        }
        bRes = (g_bRecurse||bRes) && CopyCeToWin32(args, dst);
    }
    else {
        debug("program must be called either 'pput' or 'pget'\n");
        return 1;
    }

    CeRapiUninit();

    return bRes ? 0 : 1;
}


