#!/bin/ch
/* For creating Ch Ming 1.0, Feb 10, 2006

    this script does the following:
    + Copies headers from <MING_HOME>/src/include to ./chming/include subfolder
    + Copies Ming DLLs to ./chming/bin subfolders
    + Copies C/Ch samples from ./demos/ directory to ./chming/demos
    + Generates *.h files in ./chfcreate/ directory, those files contain a 
      list of function prototypes for Ming. This list is based off of ming.h.
    + Processes each function list with c2chf
    + Moves *.chf files generated by c2chf to ./chming/lib subfolder and
      *_chdl.c files to ./c subfolder
    + Builds .dl files using Ch SDK and places them to ./chming/dl
*/

#include <unistd.h>   // for access()
#include <dlfcn.h>    // for dlopen()
#include <chshell.h>  // for chinfo()

int addChdlHeader(char *headerfile, char *macro, char *chdlheader);
int removeFuncProto(char *filename, char *funcname, int keepNum);
int changeChfFiles(char *IncDir, char *LibDir);

string_t pkgname="chming";      // define package name
chinfo_t info;                  // for Ch version number
string_t cwd = _cwd;            // the current working directory
string_t lib_header_dir;        // library header files directory
string_t debugFile;             // compilation debug information in Windows
string_t debug;
string_t makecmd;
int i;
#if defined(_WIN32_)
    debug=">nul 2>nul";         // surpress messages during cleaning  in Windows
    debugFile =">logfile 2>&1"; // compilation debug information in 'logfile'
    makecmd = "nmake -f Makefile.win";
#else
    makecmd = "make -f Makefile";
#endif


//make sure pgkcreate.ch is run from the current working directory
if(access("pkgcreate.ch", R_OK)) {
    echo Run ./pkgcreate.ch in the current directory.
    exit(-1);
}
// run this script in proper Ch version
chinfo(&info);
if ((info.vermajor*100+ info.verminor*10 +  info.vermicro) < 501) {
   echo "To run this script, you need to install Ch version 5.0.1.12201 or higher"
   echo "You can download the latest version from http://www.softintegration.com/download"
   exit(-1);
}


echo Cleaning up existing directories and create new ones ...
if (!access(pkgname, F_OK))
  rm -rf $pkgname

/* create directories */
echo create $pkgname directories
mkdir $pkgname
mkdir $pkgname/dl $pkgname/lib $pkgname/include $pkgname/demos $pkgname/bin
if(access("chfcreate", R_OK)) {
   mkdir chfcreate
}

echo Copying header files ...
cp -f ../src/ming.h  	       $pkgname/include
cp -f ../src/ming_config.h     $pkgname/include

// if the original package doesn't have the modiciation for Ch
if(`grep LOAD_CHDL $pkgname/include/ming.h` == NULL)
{
    echo Adding LOAD_CHDL into ming.h ...
    char *macro ="#define Ming_H";
    char *chdlheader =
   "\n\n"
   "#ifdef _CH_\n"
   "#pragma package <chming> \n"
   "#include <chdl.h> \n"
   "LOAD_CHDL(ming); \n"
   "#endif \n\n";
   addChdlHeader(stradd(pkgname, "/include/ming.h"), macro, chdlheader);
}

echo Copying demo programs ...
cp -fr demos/*        $pkgname/demos
chmod 755 $pkgname/demos/*.c*
if(!access(stradd(pkgname, "/demos/CVS"), R_OK)) {
   rm -rf $pkgname/demos/CVS
}

//echo copying DLLs ...
#if defined(_WIN32_)
    //if there is dynamical library pkgname 
    cp -f ../bin/*.dll $pkgname/bin/ >nul 2>&1
#endif

/* ming function does not have a common return type */
echo Creating a header file containing all functions to export ...
char *returnTypes[]={"int", "void", "float",
   "SWFBlock", "SWFMatrix", "SWFInput", "SWFCharacter", "SWFDBLBitmap",
   "SWFDBLBitmapData", "SWFJpegBitmap", "SWFJpegWithAlpha", "SWFGradient",
   "SWFFillStyle", "SWFLineStyle", "SWFShape", "SWFMorph", "SWFFont",
   "SWFText", "SWFBrowserFont", "SWFFontCharacter", "SWFTextField",
   "SWFSoundStream", "SWFSound", "SWFSoundInstance", "SWFCXform", "SWFAction",
   "SWFButton", "SWFSprite", "SWFPosition", "SWFDisplayItem", "SWFFill",
   "SWFMovieClip", "SWFMovie", "SWFVideoStream", NULL};

char *fileName;
rm -rf ./chfcreate/ming.h

for(i=0;returnTypes[i]!=NULL;i++)
{
   
   fileName = stradd("chfcreate/", returnTypes[i], ".h");
   processhfile(returnTypes[i], 1, ";", stradd(pkgname, "/include/ming.h"), 
      fileName, NULL);
   cat $fileName >> ./chfcreate/ming.h
   rm -rf $fileName
}


echo Removing duplicates in ming.h ...
removeFuncProto("chfcreate/ming.h", "SWFSoundInstance_setNoMultiple", 1);

echo Removing special function prototypes in ming.h ...
/* depreceated */
removeFuncProto("chfcreate/ming.h", "SWFMovie_outputC", 0);
/* have pointer to function*/
removeFuncProto("chfcreate/ming.h", "Ming_setWarnFunction", 0);
removeFuncProto("chfcreate/ming.h", "Ming_setErrorFunction", 0);
removeFuncProto("chfcreate/ming.h", "SWFMovie_output", 0);

echo Generating ming_chdl.c in c/ directory and xxx.chf in $pkgname/lib ...
c2chf chfcreate/ming.h -o c c -o chf $pkgname/lib

echo Patching special .chf files ...
cp -f chfhandmade/*.chf $pkgname/lib
chmod 644 $pkgname/lib/*.chf

echo Patching .chf files that return pointer to structure ...
changeChfFiles("../src", stradd(pkgname, "/lib"));

echo Building the dynamically loaded library ...
cd c
$makecmd clean $debug
$makecmd $debugFile

// go back to original directory
cd $cwd

echo Adding .DLL path and testing .dl file ...
#pragma exec _path=stradd(_path, "chming/bin");
if (dlopen("c/libming.dl", RTLD_LAZY) == NULL) {
    printf("Error: test of loading libming.dl: %s\n", dlerror());
    exit(-1);
}

mv -f c/libming.dl "$cwd/$pkgname/dl"

echo package $pkgname created successfully!


/*******************************************************************
 * * This function will add the required preprocessing directives
 * * needed by Ch to a header file.
 * * headerfile:   header file to be changed
 * * macro:        token to be recoganized for insertion of chdlheader
 * * chdlheader:   preprocessing directives to be inserted
 * *******************************************************************/
int addChdlHeader(char *headerfile, char *macro, char *chdlheader)
{
   char *stop_ptr;
   string_t tempfile, token;
   string_t result;
   FILE *stream;
                            
   tempfile = ``cat $headerfile``;
   token = strstr( tempfile, macro);
   stop_ptr = strstr(token, "\n");
   strncpy(result, tempfile, strlen(tempfile)-strlen(stop_ptr));
   result=stradd(result, chdlheader);
   result=stradd(result, stop_ptr);
   if(!(stream = fopen("_tmpfile", "w")))
   {
      perror("_tmpfile");
      return -1;
   }
   fprintf(stream, "%s", result);
   fclose(stream);
   mv -f _tmpfile $(headerfile)
   return 0;
}


/*************************************************************************
 * (C) Copyright 2005 SoftIntegration, Inc.
 * This function is in public domain, use as it is without any warranty.
 ************************************************************************** 
 * This function removes function prototypes such as
 *     int func1(int i);
 * from a header file that has already been filtered through processhfile().
 *
 * Argumnets:
 *    filename: name of the file to process
 *    funcname: function name to look for
 *    keepNum: if 0, remove all occurances of this prototype.
 *             if >0 , keep the keepNum desired funcname that is found.
 * Example:
 *    removeFuncProto("chfcreate/test.h", "func1", 2);
 * This will remove all instances except for the 2nd instance of the function, 
 * func1(), prototype that is found in the file chfcreate/test.h.
 ***************************************************************/
#define LINESIZE 1024
int removeFuncProto(char *filename, char *funcname, int keepNum)
{
   char line[LINESIZE], *ptr;
   string_t filenameout;
   FILE *stream1, *stream2;
   int num = 0;

   /* Check to see if the funcname provided is NULL */
   if (funcname == NULL)
   {
      printf("Error: %s: Null funcname recieved.\n", __func__);
      return -1;
   }

   /* Check for access to the file */
   if(!(stream1 = fopen(filename, "r")))
   {
      printf("Error: %s: Unable to open '%s' for reading.\n", __func__, filename);
      return -1;
   }

   filenameout = tmpnam(NULL);
   /* Check for access to the file */
   if(!(stream2 = fopen(filenameout, "w")))
   {
      printf("Error: %s: Unable to open '%s' for writing.\n", __func__, filenameout);
      return -1;
   }

    fgets(line, LINESIZE , stream1);
    while(!feof(stream1)) {
        if(ptr = strstr(line, funcname)) {
           if(*(ptr-1) == ' ' || *(ptr-1) == '\t' || /* find ' ' in "int' 'funcname();" */
              *(ptr-1) == '*')     /* find * in "int *funcname();" */
           {
              ptr += strlen(funcname);
              while(*ptr == ' ')         /* skip ' ' in "int funcname' '();" */
              {
                 ptr++;
              }
              if(*ptr == '(') {          /* Find '(' in "int funcname();" */
                 num++;
                 if(num != keepNum) {
                   while(!strchr(line, ';'))      /* till find ';' in "int funcname();" */
                   {
                     fgets(line, LINESIZE , stream1);
                   }
                   fgets(line, LINESIZE , stream1);
                   continue;
                 }
              }
           }
        }
        fputs(line, stream2);
        fgets(line, LINESIZE , stream1);
    }
    fclose(stream1);
    fclose(stream2);
   /* Move the temperary file to the original file. */
    mv -f $filenameout $filename
    return 0;
}

/*******************************************************************
* This function will change the chf files so that they return
* a NULL instead of a -1 when a pointer to structure is returned by the 
* function.
* The list of known structures are normally provided in the ming.h 
* header file.
*
* IncDir: Directory where ming.h is located
* LibDir: Directory where the chf files should be modified
*******************************************************************/
int changeChfFiles(char *IncDir, char *LibDir)
{
   string_t structList;
   string_t tempStructList;
   string_t tempStr;
   string_t chfList;
   string_t chfFile;
   string_t output;
   string_t workingDir;
   char *chfFileName;
   char *tempChrPtr;
   char *test;
   char *startChrPtr;
   char *endChrPtr;
   char *macro = "return -1;";
   char *mingHeaderLocation = stradd(IncDir, "/ming.h");
   FILE *fptr;
   
   workingDir = _cwd;

   if(access(mingHeaderLocation, R_OK)) 
   {
       printf("Error: Unable to find '%s'.\n"
              "Chf files will not be modified.\n", 
              mingHeaderLocation);
       return -1;
   }

   if(access(LibDir, R_OK)) 
   {
       printf("Error: Unable to find '%s'.\n"
              "Chf files will not be modified.\n", 
              LibDir);
       return -1;
   }

   tempStructList = `grep "typedef struct" $mingHeaderLocation`; 

   foreach(tempChrPtr; tempStructList; NULL; ";")
   {

       if( (tempChrPtr = strstr(tempChrPtr, "*")) == NULL)
       {
           continue;
       }

       tempChrPtr += 1;
       endChrPtr = strstr(tempChrPtr, ";"); 
       
       strncpy(tempStr, tempChrPtr, strlen(tempChrPtr)-strlen(endChrPtr));
       structList = stradd(structList, tempStr, " ");
   }

#ifdef DEBUG
   printf("%s\n", structList);
#endif

   cd $LibDir

#ifdef DEBUG
   printf("\tModifying Chf Files...\n");
#endif
   
   foreach(tempChrPtr; structList; NULL; " ")
   {
       test = stradd(tempChrPtr, " retval");
       chfList = `grep -l "$test" *.chf`;

#ifdef DEBUG
       printf("looking for %s\n", tempChrPtr);
       printf("%s\n", chfList);
#endif

       foreach(chfFileName; chfList; NULL; " ")
       {

#ifdef DEBUG
           printf("testing file %s\n", chfFileName);
#endif

           chfFile = ``cat $chfFileName``;

           if( (startChrPtr = strstr(chfFile, macro)) == NULL)
           {
               printf("\t%s not found in %s.\n", macro, chfFileName);
               continue;
           }

           strncpy(output, chfFile, strlen(chfFile)-strlen(startChrPtr));
           startChrPtr += strlen(macro);
           output = stradd(output, "return NULL;", startChrPtr);

           fptr = fopen("temp12345.txt", "w");
           fprintf(fptr, "%s", output);
           fclose(fptr);
           mv -f temp12345.txt $chfFileName
       }
   }
   
   cd $workingDir

   return 0;
}
