// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2022 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see .
/*
* SetVersion.cpp
* boinc
*
*/
// Important: To ensure that the relevant *info.plist and *InfoPlist.strings
// files are available by the time they are needed for building a target:
// [1] include SetVersion in that target's "Target Dependencies" phase,
// [2] if a target is dependent on a file created by SetVersion, make sure
// the file is listed as an Output File for SetVersion's script phase.
// Otherwise, the build may fail due to a race condition.
//
// Also, make sure the template used by SetVersion to create the file exists
// in clientgui/mac/templates.
//
// Set STAND_ALONE TRUE if testing as a separate applicaiton
#define STAND_ALONE 0
#define VERBOSE_SPAWN 0 /* for debugging callPosixSpawn */
#include
#include
#include
#include
#include
#include
#include // for MAXPATHLEN
#include
#include "version.h"
int file_exists(const char* path);
int FixInfoPlistFile(char* name);
int FixInfoPlist_Strings(char* myPath, char* name);
int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand);
int MakeBOINCRestartPackageInfoPlistFile(char* myPath, char* brand);
int MakeMetaPackageInfoPlistFile(char* myPath, char* brand);
int callPosixSpawn(const char *cmd);
int main(int argc, char** argv) {
int retval = 0, err;
#if STAND_ALONE
char myPath[1024];
getcwd(myPath, sizeof(myPath));
printf("%s\n", myPath); // For debugging
#endif
if (!file_exists("./English.lproj")) {
retval = mkdir("./English.lproj", 0755);
if (retval) {
printf("Error %d creating directory English.lproj\n", retval);
}
}
// BOINC Manager
err = FixInfoPlist_Strings("./English.lproj/InfoPlist.strings", "BOINC Manager");
if (err) retval = err;
err = FixInfoPlistFile("Info.plist");
if (err) retval = err;
// BOINC Installer
err = FixInfoPlist_Strings("./English.lproj/Installer-InfoPlist.strings", "BOINC Installer");
if (err) retval = err;
err = FixInfoPlistFile("Installer-Info.plist");
if (err) retval = err;
// BOINC PostInstall app
err = FixInfoPlist_Strings("./English.lproj/PostInstall-InfoPlist.strings", "Install BOINC");
if (err) retval = err;
err = FixInfoPlistFile("PostInstall-Info.plist");
if (err) retval = err;
// BOINC Screen Saver
err = FixInfoPlist_Strings("./English.lproj/ScreenSaver-InfoPlist.strings", "BOINC Screen Saver");
if (err) retval = err;
err = FixInfoPlistFile("ScreenSaver-Info.plist");
if (err) retval = err;
// BOINC Uninstaller
err = FixInfoPlist_Strings("./English.lproj/Uninstaller-InfoPlist.strings", "Uninstall BOINC");
if (err) retval = err;
err = FixInfoPlistFile("Uninstaller-Info.plist");
if (err) retval = err;
// SystemMenu is not currently used
err = FixInfoPlistFile("SystemMenu-Info.plist");
if (err) retval = err;
// BOINCCmd
err = FixInfoPlistFile("BoincCmd-Info.plist");
if (err) retval = err;
// WaitPermissions is not currently used
err = FixInfoPlistFile("WaitPermissions-Info.plist");
if (err) retval = err;
// The following are not used by Xcode, and are probably obsolete
err = MakeBOINCPackageInfoPlistFile("./Pkg-Info.plist", "BOINC Manager");
if (err) retval = err;
err = MakeBOINCRestartPackageInfoPlistFile("./Pkg_Restart-Info.plist", "BOINC Manager");
if (err) retval = err;
err = MakeMetaPackageInfoPlistFile("./Mpkg-Info.plist", "BOINC Manager");
return retval;
}
int file_exists(const char* path) {
struct stat buf;
if (stat(path, &buf)) {
return false; // stat() returns zero on success
}
return true;
}
int FixInfoPlist_Strings(char* myPath, char* name) {
int retval = 0;
FILE *f;
time_t cur_time;
struct tm *time_data;
cur_time = time(NULL);
time_data = localtime( &cur_time );
f = fopen(myPath, "w");
if (f)
{
fprintf(f, "/* Localized versions of Info.plist keys */\n\n");
fprintf(f, "CFBundleName = \"%s\";\n", name);
fprintf(f, "CFBundleShortVersionString = \"%s version %s\";\n", name, BOINC_VERSION_STRING);
fprintf(f, "CFBundleGetInfoString = \"%s version %s, Copyright %d University of California.\";\n", name, BOINC_VERSION_STRING, time_data->tm_year+1900);
fflush(f);
retval = fclose(f);
}
else {
printf("Error creating file %s\n", myPath);
retval = -1;
}
return retval;
}
int FixInfoPlistFile(char* name) {
int retval = 0;
FILE *fin = NULL, *fout = NULL;
char *c, a, buf[1024];
char srcPath[MAXPATHLEN], dstPath[MAXPATHLEN];
strcpy(dstPath, "./");
strcat(dstPath, name);
strcpy(srcPath, "../clientgui/mac/templates/");
strcat(srcPath, name);
// Save the old file in case there is an error updating it
if (file_exists(dstPath)) {
rename(dstPath, "./temp");
}
fin = fopen(srcPath, "r");
if (fin == NULL)
goto bail;
fout = fopen(dstPath, "w");
if (fout == NULL) {
goto bail;
}
// Copy everything up to version number
for (;;) {
c = fgets(buf, sizeof(buf), fin);
if (c == NULL)
goto bail; // EOF
c = strstr(buf, "CFBundleVersion");
if (c)
break; // Found "CFBundleVersion"
fputs(buf, fout);
}
c = strstr(buf, "");
if (c == NULL) {
fputs(buf, fout);
c = fgets(buf, sizeof(buf), fin);
if (c == NULL)
goto bail; // EOF
c = strstr(buf, "");
if (c == NULL)
goto bail; // "CFBundleVersion" not followed by ""
}
a = *(c+8);
*(c+8) = '\0'; // Put terminator after ""
fputs(buf, fout); // Copy up to end of ""
fputs(BOINC_VERSION_STRING, fout); // Write the current version number
*(c+8) = a; // Undo terminator we inserted
c = strstr(buf, ""); // Skip over old version number in input
fputs(c, fout); // Copy rest of input line
// Copy rest of file
for (;;) {
c = fgets(buf, sizeof(buf), fin);
if (c == NULL)
break; // EOF
fputs(buf, fout);
}
fclose(fin);
fflush(fout);
fclose(fout);
unlink("temp");
return retval;
bail:
if (fin)
fclose(fin);
if (fout)
fclose(fout);
if (file_exists("./temp")) {
rename("./temp", dstPath);
// sprintf(buf, "mv -f temp %s", myPath);
// retval = callPosixSpawn(buf);
} else {
sprintf(buf, "cp -f %s %s", srcPath, dstPath);
retval = callPosixSpawn(buf);
}
printf("Error updating version number in file %s\n", dstPath);
return -1;
}
int MakeBOINCPackageInfoPlistFile(char* myPath, char* brand) {
int retval = 0;
FILE *f;
f = fopen(myPath, "w");
if (f)
{
fprintf(f, "\n");
fprintf(f, "\n");
fprintf(f, "\n\n");
fprintf(f, "\tCFBundleGetInfoString\n");
fprintf(f, "\t%s %s\n", brand, BOINC_VERSION_STRING);
fprintf(f, "\tCFBundleIdentifier\n\tedu.berkeley.boinc\n");
fprintf(f, "\tCFBundleShortVersionString\n");
fprintf(f, "\t%s\n", BOINC_VERSION_STRING);
fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n");
fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n");
fprintf(f, "\tIFPkgFlagDefaultLocation\n\t/\n");
fprintf(f, "\tIFPkgFlagFollowLinks\n\t0\n");
fprintf(f, "\tIFPkgFlagInstallFat\n\t0\n");
fprintf(f, "\tIFPkgFlagInstalledSize\n\t6680\n");
fprintf(f, "\tIFPkgFlagIsRequired\n\t0\n");
fprintf(f, "\tIFPkgFlagOverwritePermissions\n\t0\n");
fprintf(f, "\tIFPkgFlagRelocatable\n\t0\n");
fprintf(f, "\tIFPkgFlagRestartAction\n\tNoRestart\n");
fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n");
fprintf(f, "\tIFPkgFlagUpdateInstalledLanguages\n\t0\n");
fprintf(f, "\tIFPkgFormatVersion\n\t0.10000000149011612\n");
fprintf(f, "\n\n");
fflush(f);
retval = fclose(f);
}
else {
printf("Error creating file %s\n", myPath);
retval = -1;
}
return retval;
}
// Create a MetaPackage which runs only BOINC,pkg but specifies Restart Required
int MakeBOINCRestartPackageInfoPlistFile(char* myPath, char* brand) {
int retval = 0;
FILE *f;
f = fopen(myPath, "w");
if (f)
{
fprintf(f, "\n");
fprintf(f, "\n");
fprintf(f, "\n\n");
fprintf(f, "\tCFBundleGetInfoString\n");
fprintf(f, "\t%s %s\n", brand, BOINC_VERSION_STRING);
fprintf(f, "\tCFBundleIdentifier\n\tedu.berkeley.boinc_r\n");
fprintf(f, "\tCFBundleShortVersionString\n");
fprintf(f, "\t%s\n", BOINC_VERSION_STRING);
fprintf(f, "\tIFMajorVersion\n\t%d\n", BOINC_MAJOR_VERSION);
fprintf(f, "\tIFMinorVersion\n\t%d\n", BOINC_MINOR_VERSION);
fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n");
fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n");
fprintf(f, "\tIFPkgFlagRestartAction\n\tRequiredRestart\n");
fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n");
fprintf(f, "\tIFPkgFlagComponentDirectory\n\t../\n");
fprintf(f, "\tIFPkgFlagPackageList\n");
fprintf(f, "\t\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\t\tIFPkgFlagPackageLocation\n\t\t\tBOINC.pkg\n");
fprintf(f, "\t\t\tIFPkgFlagPackageSelection\n\t\t\trequired\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\n");
fprintf(f, "\tIFPkgFormatVersion\n\t0.10000000149011612\n");
fprintf(f, "\n\n");
fflush(f);
retval = fclose(f);
}
else {
printf("Error creating file %s\n", myPath);
retval = -1;
}
return retval;
}
// Make a MetaPackage to install both BOINC and VirtualBox
int MakeMetaPackageInfoPlistFile(char* myPath, char* brand) {
int retval = 0;
FILE *f;
f = fopen(myPath, "w");
if (f)
{
fprintf(f, "\n");
fprintf(f, "\n");
fprintf(f, "\n\n");
fprintf(f, "\tCFBundleGetInfoString\n");
fprintf(f, "\t%s %s + VirtualBox\n", brand, BOINC_VERSION_STRING);
fprintf(f, "\tCFBundleIdentifier\n\tedu.berkeley.boinc+vbox\n");
fprintf(f, "\tCFBundleShortVersionString\n");
fprintf(f, "\t%s\n", BOINC_VERSION_STRING);
fprintf(f, "\tIFMajorVersion\n\t%d\n", BOINC_MAJOR_VERSION);
fprintf(f, "\tIFMinorVersion\n\t%d\n", BOINC_MINOR_VERSION);
fprintf(f, "\tIFPkgFlagAllowBackRev\n\t1\n");
fprintf(f, "\tIFPkgFlagAuthorizationAction\n\tAdminAuthorization\n");
fprintf(f, "\tIFPkgFlagRestartAction\n\tNoRestart\n");
fprintf(f, "\tIFPkgFlagRootVolumeOnly\n\t1\n");
fprintf(f, "\tIFPkgFlagComponentDirectory\n\t../\n");
fprintf(f, "\tIFPkgFlagPackageList\n");
fprintf(f, "\t\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\t\tIFPkgFlagPackageLocation\n\t\t\tBOINC.pkg\n");
fprintf(f, "\t\t\tIFPkgFlagPackageSelection\n\t\t\trequired\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\t\tIFPkgFlagPackageLocation\n\t\t\tVirtualBox.pkg\n");
fprintf(f, "\t\t\tIFPkgFlagPackageSelection\n\t\t\tselected\n");
fprintf(f, "\t\t\n");
fprintf(f, "\t\n");
fprintf(f, "\tIFPkgFormatVersion\n\t0.10000000149011612\n");
fprintf(f, "\n\n");
fflush(f);
retval = fclose(f);
}
else {
printf("Error creating file %s\n", myPath);
retval = -1;
}
return retval;
}
#define NOT_IN_TOKEN 0
#define IN_SINGLE_QUOTED_TOKEN 1
#define IN_DOUBLE_QUOTED_TOKEN 2
#define IN_UNQUOTED_TOKEN 3
static int parse_posix_spawn_command_line(char* p, char** argv) {
int state = NOT_IN_TOKEN;
int argc=0;
while (*p) {
switch(state) {
case NOT_IN_TOKEN:
if (isspace(*p)) {
} else if (*p == '\'') {
p++;
argv[argc++] = p;
state = IN_SINGLE_QUOTED_TOKEN;
break;
} else if (*p == '\"') {
p++;
argv[argc++] = p;
state = IN_DOUBLE_QUOTED_TOKEN;
break;
} else {
argv[argc++] = p;
state = IN_UNQUOTED_TOKEN;
}
break;
case IN_SINGLE_QUOTED_TOKEN:
if (*p == '\'') {
if (*(p-1) == '\\') break;
*p = 0;
state = NOT_IN_TOKEN;
}
break;
case IN_DOUBLE_QUOTED_TOKEN:
if (*p == '\"') {
if (*(p-1) == '\\') break;
*p = 0;
state = NOT_IN_TOKEN;
}
break;
case IN_UNQUOTED_TOKEN:
if (isspace(*p)) {
*p = 0;
state = NOT_IN_TOKEN;
}
break;
}
p++;
}
argv[argc] = 0;
return argc;
}
#include
int callPosixSpawn(const char *cmdline) {
char command[1024];
char progName[1024];
char progPath[MAXPATHLEN];
char* argv[100];
int argc __attribute__((unused)) = 0;
char *p;
pid_t thePid = 0;
int result = 0;
int status = 0;
extern char **environ;
// Make a copy of cmdline because parse_posix_spawn_command_line modifies it
strlcpy(command, cmdline, sizeof(command));
argc = parse_posix_spawn_command_line(const_cast(command), argv);
strlcpy(progPath, argv[0], sizeof(progPath));
strlcpy(progName, argv[0], sizeof(progName));
p = strrchr(progName, '/');
if (p) {
argv[0] = p+1;
} else {
argv[0] = progName;
}
#if VERBOSE_SPAWN
printf("***********");
for (int i=0; i