/*************************************************************************
 ***                            Install.cmm                            ***
 *** This is the installation program to set up CEnvi unregistered     ***
 *** shareware on your computer.  Before running this program, CEnvi   ***
 *** must be copied to a hard disk directory.  This Install.cmm        ***
 *** program is identical for DOS, OS/2, and Windows version of CEnvi  ***
 *** and this program is itself an example for how to use CEnvi        ***
 *************************************************************************/


main(argc,argv)
{
   // There should be no input args.  If there are then the user needs help.
   ScreenClear();
   if ( argc != 1 )
      GiveInstructionsAndExit();

   // Get the current directory.  The GetInstallDirectory() function assures
   // that it is not a floppy.
   InstallDirectory = GetInstallDirectory(argv[0]);

   if defined( _DOS_ )
      DosOrOS2Install(InstallDirectory,"C:\\AUTOEXEC.BAT","C:\\AUTOEXEC.BAK");
   else if defined( _OS2_)
      DosOrOS2Install(InstallDirectory,"C:\\CONFIG.SYS","C:\\CONFIG.BAK");
   else {
      assert( defined(_WINDOWS_) );
      WindowsInstall(InstallDirectory);
      printf("\nPress any key to exit...");
      getch();
   }
   return(EXIT_SUCCESS);
}


GiveInstructionsAndExit()  // Show some info about using this program
{                          // and then exit this program. Do not return.
   printf("Install.cmm - CEnvi installation procedure.  Execute with no parameters\n");
   printf("              from the directory that contains CEnvi.exe and also contains\n");
   printf("              the *.cmm and other sample CEnvi files, including Install.cmm\n");
   if defined( _WINDOWS_ ) {
      printf("\nPress any key to exit...");
      getch();
   }
   exit(EXIT_FAILURE);
}


GetInstallDirectory(Executable)   // return current dir that is not on a floppy
{
   // current source directory from the name of the executable
   CurDir = SplitFileName(FullPath(Executable)).dir;
   assert( CurDir != NULL  &&  CurDir[0] != 0 );

   // check that current directory is not floppy A: or B:
   if ( CurDir[0] == 'A'  ||  CurDir[0] == 'B' ) {
      printf("\nCannot install CEnvi from a floppy.\n\a\n");
      GiveInstructionsAndExit();
   }

   // Have found the directory Install.cmm is in, and so check that
   // CEnvi.exe is also in the same directory.
   if ( NULL == Directory(strcat(strcpy(temp,CurDir),"CEnvi.exe")) ) {
      printf("\nThis installation assumes that CEnvi.exe is in the same directory as\n");
      printf("Install.cmm.  Could not find CEnvi.exe in the Install.cmm directory.\n\a\n");
      GiveInstructionsAndExit();
   }

   // remove extra backslash if at end of name (i.e., not root directory)
   if ( strcmp(CurDir+1,":\\") )
      CurDir[strlen(CurDir)-1] = 0;

   // return the result
   return(CurDir);
}


Fatal(Format)  // like printf() but also beeps and exits program
{
   va_start(parameters,Format);
   printf("\a\n");  // beep
   vprintf(Format,parameters);
   exit(EXIT_FAILURE);
}


GetYesOrNo()   // prompt for Yes or No, and return entered boolean TRUE
{              // if YES else FALSE if NO.
   printf(" (Y/N) ");
   while( TRUE ) { // forever
      key = toupper(getch());
      if ( key != 'Y' && key != 'N' )
         printf("\a");  // beep
      else
         break;
   }
   printf("%c\n",key);
   return( key == 'Y' );
}


CopyFile(Source,Destination) // copy file from source to destination
{
   // open source and destination files
   src = fopen(Source,"rb");
   if ( src == NULL )
      Fatal("Could not open source file \"%s\" for reading.",Source);
   dest = fopen(Destination,"wb");
   if ( dest == NULL )
      Fatal("Could not open file \"%s\" for writing.",Destination);

   // read chunks from source, and copy to destination
   #define  COPY_CHUNK_SIZE   500
   while ( 0 != (size = fread(buf,COPY_CHUNK_SIZE,src)) ) {
      fwrite(buf,size,dest);
   }

   // close the files
   fclose(dest);
   fclose(src);
}


SkipWhitespace(str)
{
   while( isspace(str[0]) )
      str++;
}


AlreadyInPath(Dir,Path) // search through path for this Dir, return True if found, else False
{
   len = strlen(Dir)
   p = Path;
   do {
      if ( 0 == strnicmp(p,Dir,len)  && (p[len]==0 || p[len]==';') )
         return(True)
      p = strchr(p,';')
   } while( p++ != NULL )
   return(False)
}


AlterEVarLine(text,EVar,Dir)
   // If this text is a line setting the EVar environment variable, and if
   // Dir is not already in it, then add Dir.  Return TRUE if this is
   // a line for EVar, and False if it is not.
{
   FoundEVar = FALSE; // assume this is not the line
   // determine if text is of the type "EVAR=" or "set EVAR="
   SkipWhitespace(c = text);
   // if the next statement is "SET" then skip it
   if ( !strncmpi(c,"SET",3) ) {
      // Skip beyond the SET statement
      c += 3;
      SkipWhitespace(c);
   }
   // test if this next part is the variable name
   EVarLen = strlen(EVar);
   if ( !strncmpi(c,EVar,EVarLen) ) {
      c += EVarLen;
      if ( isspace(c[0])  ||  c[0] == '=' ) {
         // THIS IS IT.  This line describes the EVar.
         SkipWhitespace(c);
         if ( c[0] == '=' ) {
            c++;
            SkipWhitespace(c);
         }
         FoundEVar = TRUE;
         // If Dir is not already in this line then add Dir at the end.
         // Check each dir as value between semicolons (;)
         SkipWhitespace(c);
         if ( !AlreadyInPath(Dir,c) ) {
            // add this to the end of the existing path
            if ( c[strlen(c)-1] != ';' )
               strcat(c,";");
            strcat(c,Dir);
         }
      }
   }
   return(FoundEVar);
}

DosOrOS2Install(Dir,FileName,BackupFileName)
{
   // append the PATH statement so that it contains this directory, and
   // also add the CMMPATH environment variable.  But give user a choice
   // of whether to do this first; if they choose not to then tell them
   // what they must do by hand.
   printf("\nIf you choose, install will add the directory:\n\n");
   printf("    \"%s\"\n\n",Dir);
   printf("to the PATH environment variable in your %s file.  Install will\n",FileName);
   printf("also add this directory to the CMMPATH environment variable in that\n");
   printf("file.  If you select to make these changes to %s, then\n",FileName);
   printf("install will backup the current version of %s to %s.\n",FileName,BackupFileName);
   printf("\n\nShould these changes be made to %s?",FileName);
   if ( GetYesOrNo() ) {
      printf("Making changes to %s...",FileName);
      // user chose to make automatic changes.  Do so now
      CopyFile(FileName,BackupFileName);

      // will now read the backup file, and if any PATH or CMMPATH line is
      // encountered then will alter it, else just copy line exactly.
      src = fopen(BackupFileName,"rt");
      if ( src == NULL )
         Fatal("Could not open source file \"%s\" for reading.",BackupFileName);
      dest = fopen(FileName,"wt");
      if ( dest == NULL )
         Fatal("Could not open source file \"%s\" for reading.",BackupFileName);
      SetPath = SetCmmPath = False;
      while ( NULL != (line = fgets(src)) ) {
         // remove the pesky newline if there is one
         if ( PeskyNewLine = (line[strlen(line)-1] == '\n') )
            line[strlen(line)-1] = 0;
         if ( AlterEVarLine(line,"PATH",Dir) )
            SetPath = True;
         if ( AlterEVarLine(line,"CMMPATH",Dir) )
            SetCmmPath = True;
         fputs(line,dest);
         if ( PeskyNewLine )
            fputs("\n",dest);
      }
      fclose(src);
      // if haven't already written PATH or CMMPATH, then do so now
      if ( !SetPath )
         fprintf(dest,"\nSET PATH=%s\n",Dir);
      if ( !SetCmmPath )
         fprintf(dest,"\nSET CMMPATH=%s\n",Dir);
      fclose(dest);
      printf("\n%s has been altered.\n",FileName);
      printf("Changes will take effect after you reboot.\n");
   } else {
      // user choose not to automatically install, so describe what the user
      // needs to do by hand to make it work.
      ScreenClear();
      printf("\nThe PATH environment variable is used to find executable files, such as\n");
      printf("CEnvi.exe, and batch files, such as those included in this CEnvi\n");
      printf("unregistered shareware release.  The CMMPATH environment variable is\n");
      printf("used by CEnvi to find common source code that may be included\n");
      printf("in other CEnvi source files.\n\n");
      printf("You chose not to have Install automatically alter %s.\n\n",FileName);
      printf("You may wish to try Install.cmm again, or to make these modifications\n");
      printf("to %s by hand.\n",FileName);
   }
}


WindowsInstall(Dir)
{
   // If the user chooses, then add CMM Extensions and also add
   // CMMPATH setting to WIN.INI.
   printf("\nIf you choose, install will add the directory:\n\n");
   printf("    \"%s\"\n\n",Dir);
   printf("as a CMMPATH profile string in you WIN.INI file.  Install will also\n");
   printf("also add the .CMM extension to WIN.INI so that you can execute any\n");
   printf("*.CMM file simply by double-clicking on it.\n");
   printf("\n\nShould these changes be made to WIN.INI?");
   if ( GetYesOrNo() ) {
      printf("Making changes to WIN.INI...");
      // add extensions
      sprintf(extension,"%s\\CENVI.EXE ^.CMM",Dir);
      WriteProfileString("Extensions","CMM",extension);
      // add CMMPATH
      WriteProfileString("CEnvi","CMMPATH",Dir);
      TellEveryoneThatWinIniHasChanged();
      printf("\n");
   } else {
      // user choose not to automatically install, so describe what the user
      // needs to do by hand to make it work.
      ScreenClear();
      printf("The .CMM extension in WIN.INI would allow you to execute CMM code simply by\n");
      printf("double-clicking the file name.  The CMMPATH profile string allows CEnvi to\n");
      printf("find Cmm source code or code library that may be in a different directory.\n");
      printf("\n");
      printf("You chose not to have Install automatically alter WIN.INI\n");
      printf("\n");
      printf("You may wish to make these changes by hand; if so, then add the line:\n");
      printf("     CMM=%s\\CENVI.EXE ^.CMM\n",Dir);
      printf("to the [Extensions] section of WIN.INI.  At the end of WIN.INI you may also\n");
      printf("want to add these lines:\n");
      printf("    [CEnvi]\n");
      printf("    CMMPATH=%s\n",Dir);
   }
}


WriteProfileString(ApplicationName,KeyName,KeyValue)
   // This is a wrapper for the WriteProfileString in the Windows Kernel.
{
   DynamicLink("KERNEL","WRITEPROFILESTRING",SWORD16,PASCAL,
               ApplicationName,KeyName,KeyValue);
}


TellEveryoneThatWinIniHasChanged()
{
   #define HWND_BROADCAST  0xFFFF
   #define WM_WININICHANGE 0x001A
   DynamicLink("USER","SENDMESSAGE",SWORD16,PASCAL,
               HWND_BROADCAST,WM_WININICHANGE,0,0,0);
}

