// (c) 2002-2007 Electronic Arts Inc.

#if defined(_MSC_VER)
#pragma warning(disable: 4996)  // warning C4996: 'fscanf' was declared deprecated
#endif
#if defined(__ICL)
#pragma warning(disable: 1478)  // (icl) warning #1478: function "strcpy" was declared "deprecated"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// for CString
#define WINVER 0x400
#include <afxcoll.h>

int Action   = 1;
int Quiet    = 0;
int MaxDepth = -1;
int AtDepth = -1;
int verbose  = 0;

CStringList ExcludeDirs;
CStringList FileExists;

/*
;
; ABSTRACT
;
; recurse - recurse into all subfolders and execute a command
;
; SUMMARY
;
; recurse -options [script/tool]
;     -d   startdir
;     -l<n> maximum level (depth) of subdirectories to go into
;     -a<n> specified level (depth) at execution
;     -q   quiet; no display
;     -v   verbose
;     -n   Show actions, but don't perform them
;     -x   eXcluse Directory, multiple -x's can be in command line
;
; DESCRIPTION
;
; This recurse tool goes into each subfolder and launches the tool
; or script specified.
;
; RETURNS
;
; 0 success
; non-zero for failure
;
; SEE ALSO
;
; timex, zrun
;
; SAMPLE OUTPUT
;
; recurse -n del *.exe
; Recurse by Norman Davie
;
; [D:\tools\spit] del *.exe
; [D:\tools\spit\html] del *.exe
; [D:\tools\spit\reference] del *.exe;
;
; END ABSTRACT
;
*/

int Recurse(CString szSourceDir, CString Cmd, int Depth=0)
{
    WIN32_FIND_DATA FindData;
    LPWIN32_FIND_DATA lpFindData;
    CString szName;
    CString szTargetName;
    HANDLE  hFile;
    CString szLocalSourceDir,szNewSourceDir;
    CString szLocalSourceFile,szNewSourceFile;
    CString szLocalTargetDir,szNewTargetDir;
    int filesCopied = 0;
    char buffer[1024];
    int i;
    POSITION pos;
    CString CurrentDir;

    i = szSourceDir.ReverseFind('\\');
    if (i == -1)
        CurrentDir = szSourceDir;
    else
        CurrentDir = szSourceDir.Mid(i+1);

    if (MaxDepth != -1)
    {
        if (Depth > MaxDepth)
            return -99;
    }

    for(i=0; i<ExcludeDirs.GetCount(); i++)
    {
        pos = ExcludeDirs.FindIndex(i);
        CString excludedir = ExcludeDirs.GetAt(pos);
        if (CurrentDir == excludedir)
            return -100;
    }

    if (verbose)
        printf("Dir = %s\n", szSourceDir.GetBuffer(256));

    lpFindData = &FindData;
    if (szSourceDir.Right(1) != "\\")
        szLocalSourceDir = szSourceDir;
    else
    {
        szLocalSourceDir = szSourceDir.Left(szSourceDir.GetLength()-1);
    }


    if (SetCurrentDirectory(szSourceDir) == 0)
    {
        if (! Quiet)
        {
            printf("Can't change directory to '%s'\n", szSourceDir.GetBuffer(256));
            printf("ABORTING...Press Enter to continue\n");
            gets(buffer);
        }
        exit(-4);
    }

    bool ActionNow = true;
    if (FileExists.GetCount())
    {
        ActionNow = false;
        for(i=0; i<FileExists.GetCount(); i++)
        {
            pos = FileExists.FindIndex(i);
            CString excludedir = szLocalSourceDir + "\\" + FileExists.GetAt(pos);

            hFile = FindFirstFile(excludedir,lpFindData);
            if (hFile != INVALID_HANDLE_VALUE)
            {
                ActionNow = true;
                FindClose(hFile);
            }
        }
    }
    if (AtDepth != -1)
    {
        if (Depth != AtDepth)
            ActionNow = false;
    }

    GetCurrentDirectory(sizeof(buffer), buffer);

    if (ActionNow)
    {
        if (!Quiet)
            printf("[%s] %s\n", buffer, Cmd.GetBuffer(256));
    }
    else
    {
        if (verbose)
            printf("[%s] skipped.\n", buffer);
    }

    if (Action && ActionNow) system(Cmd);

    // now do subdirs
    szLocalSourceFile = szLocalSourceDir + "\\*.*";
    hFile = FindFirstFile(szLocalSourceFile,lpFindData);
    if (hFile != INVALID_HANDLE_VALUE)
    {
        do
        {
            szName = szLocalSourceDir + "\\" + FindData.cFileName;
            if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
            {
                if ((strcmp(FindData.cFileName, ".") != 0) && (strcmp(FindData.cFileName, "..") != 0))
                {
                    filesCopied += Recurse(szName,Cmd, Depth+1);
                }
            }
        } while (FindNextFile(hFile,lpFindData) != 0);
        FindClose(hFile);
    }


    return filesCopied;
}

int main(int argc, char **argv)
{
    char OriginalDir[2048];
    char cwd[2048];
    char cmd[4096];
    int firsttime =1;
    int i;
    int validcommand;
    int startcmd;

    ExcludeDirs.RemoveAll();
    FileExists.RemoveAll();
    GetCurrentDirectory(sizeof(OriginalDir), OriginalDir);
    strcpy(cwd, OriginalDir);

    cmd[0] = '\0';
    startcmd = 1;
    while(startcmd < argc)
    {
        validcommand = 0;
        if (strcmp(argv[startcmd], "-a") == 0)
        {
            startcmd++;
            if (startcmd < argc)
            {
                AtDepth = atoi(argv[startcmd]);
                startcmd++;
                validcommand=1;
                printf("directory depth: %d\n", AtDepth);
            } else
            {
                printf("missing argument\n");
                exit(-2);
            }
        }
        if (strcmp(argv[startcmd], "-d") == 0)
        {
            startcmd++;
            if (startcmd < argc)
            {
                strcpy(cwd, argv[startcmd]);
                startcmd++;
                validcommand=1;
            } else
            {
                printf("missing argument\n");
                exit(-2);
            }
        }
        else if (strcmp(argv[startcmd], "-n") == 0)
        {
            Action = 0;
            validcommand=1;
            startcmd++;
        }
        else if (strcmp(argv[startcmd], "-q") == 0)
        {
            Quiet = 1;
            validcommand=1;
            startcmd++;
        }
        else if (strcmp(argv[startcmd], "-v") == 0)
        {
            verbose = 1;
            validcommand=1;
            startcmd++;
        }
        else if (strcmp(argv[startcmd], "-l") == 0)
        {
            startcmd++;
            if (startcmd < argc)
            {
                MaxDepth = atoi(argv[startcmd]);
                startcmd++;
                validcommand=1;
                printf("Maximum directory depth: %d\n", MaxDepth);
            } else
            {
                printf("missing argument\n");
                exit(-2);
            }
        }

        else if (strcmp(argv[startcmd], "-x") == 0)
        {
            startcmd++;
            if (startcmd < argc)
            {
                char arg[2048];
                strcpy(arg, argv[startcmd]);
                ExcludeDirs.AddTail(arg);
                startcmd++;
                validcommand=1;
                printf("Exclude directory: %s\n", arg);
            } else
            {
                printf("missing argument\n");
                exit(-2);
            }
        }
        else if (strcmp(argv[startcmd], "-f") == 0)
        {
            startcmd++;
            if (startcmd < argc)
            {
                char arg[2048];
                strcpy(arg, argv[startcmd]);
                FileExists.AddTail(arg);
                startcmd++;
                validcommand=1;
                printf("File Exists: %s\n", arg);
            } else
            {
                printf("missing argument\n");
                exit(-2);
            }
        }

        if (!validcommand)
            break;
    }

    if (! Quiet) printf("Recurse by Frank Barchard\n");

    if (argc < 2)
    {
        printf("Version " __DATE__ " " __TIME__ "\n");
        printf("%s [-d startdir] [-q] [-n] [-l #] [-f file] [-x dir] <cmd> <cmd opt...>\n", argv[0]);
        printf("-q Quiet (No display)\n");
        printf("-v Verbose (More display)\n");
        printf("-n Show actions, but don't perform them\n");
        printf("-l maximum level (depth) of subdirectories to go into\n");
        printf("-a<n> specified level (depth) at execution\n");
        printf("-x eXcluse Directory, multiple can be in command line\n");
        printf("-f if specified File exists, execute the command, multiple can be in command line\n");
        exit(-1);
    }

    for(i=startcmd; i<argc; i++)
    {
        if (firsttime)
            firsttime = 0;
        else
            strcat(cmd, " ");

        strcat(cmd, argv[i]);
    }
    if (cmd[0])
        Recurse(cwd, cmd);

    SetCurrentDirectory(OriginalDir);

    return 0;
}
