cdesktopenv/cde/programs/dtappbuilder/src/abmf/generate_code.c

2410 lines
57 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they 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 these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
* $XConsortium: generate_code.c /main/4 1996/08/27 16:16:30 mustafa $
*
* @(#)generate_code.c 1.36 04 May 1995 cde_app_builder/src/abmf
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special restrictions in a
* confidential disclosure agreement between HP, IBM, Sun, USL, SCO and
* Univel. Do not distribute this document outside HP, IBM, Sun, USL, SCO,
* or Univel without Sun's specific written approval. This document and all
* copies and derivative works thereof must be returned or destroyed at Sun's
* request.
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*
*/
/*
* generate_code.c - writes all files necessary for the project
*/
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include <time.h>
#include <ctype.h>
#include <assert.h>
#include <ab_private/util.h>
#include <ab_private/abio.h>
#include "dtb_utils.h"
#include "msg_file.h"
#include "obj_namesP.h"
#include "write_codeP.h"
#include "ui_header_fileP.h"
#include "resource_fileP.h"
#include "ui_c_fileP.h"
#include "stubs_c_fileP.h"
#include "proj_c_fileP.h"
#include "proj_header_fileP.h"
#include "make_fileP.h"
#include "connectP.h"
#include "merge_cP.h"
#include "instancesP.h"
#include "utils_header_fileP.h"
#include "utils_c_fileP.h"
#include "abmf.h"
#include "msg_cvt.h"
/*************************************************************************
** **
** Constants (#define and const) **
** **
**************************************************************************/
typedef struct
{
ISTRING file_name;
time_t mod_time;
BOOL merged;
} GenLogEntryRec, *GenLogEntry;
typedef struct
{
ISTRING log_file;
int num_entries;
int entries_size;
GenLogEntry entries;
} GenLogRec, *GenLog;
static int log_construct(GenLog);
static int log_destruct(GenLog);
static int log_sort_by_date(GenLog);
static GenLogEntry log_find_entry_by_name(GenLog, STRING file_name);
static int log_add_entry(
GenLog,
STRING file_name,
time_t mod_date,
BOOL merged
);
static int log_dump(GenLog);
/* private methods */
static int logP_release_data(GenLog);
static int logP_read(GenLog);
static int logP_write(GenLog);
static int logP_get_int_from_string(STRING * ptr_ptr);
/*************************************************************************
** **
** Private Functions (C declarations and macros) **
** **
**************************************************************************/
typedef enum
{
ABMF_SKIP_UNDEF = 0,
ABMF_SKIP_UP_TO_DATE,
ABMF_SKIP_NO_CHANGES,
ABMF_SKIP_ERR_OCCURRED,
ABMF_SKIP_WHY_NUM_VALUES /* must be last */
} ABMF_SKIP_WHY;
static int write_make_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL merge_files,
BOOL check_dates,
BOOL * makeFileChangedPtr
);
static int write_project_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL merge_files,
BOOL check_dates,
BOOL force_write_c_file,
BOOL * headerFileChangedPtr,
BOOL * stubsCFileChangedPtr
);
static int write_module_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj module,
BOOL merge_files,
BOOL check_dates,
STRING resFileName,
BOOL * headerFileChangedPtr,
BOOL * uiCFileChangedPtr,
BOOL * stubsCFileChangedPtr
);
static int write_utils_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL check_dates,
BOOL *headerFileChangedPtr,
BOOL *CFileChangedPtr
);
static int write_app_resource_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL mergeFiles,
BOOL checkDates,
BOOL *fileChangedOut
);
static int write_msg_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL checkDates,
BOOL *msgFileChangedOut
);
static int print_progress_message(
int verbosity,
STRING message,
STRING fileName
);
static int print_backing_up_message(
STRING fromFile,
STRING toFile
);
static int print_skipping_message(
STRING fileName,
ABMF_SKIP_WHY why
);
static int count_possible_substruct_fields(ABObj obj);
static int compare_file_times(
STRING input_file,
STRING output_file,
GenLog genLog
);
static int should_write_file(
STRING output_file,
STRING input_file,
GenLog genLog,
BOOL merging,
BOOL check_dates
);
static int check_and_merge_c_file(
BOOL needMerge,
File *codeFileInOut,
STRING codeFileName,
ABMF_SKIP_WHY *fileSkipReasonInOut,
File *deltaFileOut
);
static int check_and_replace_file(
STRING fileName,
GenLog genLog,
File newFile,
BOOL check_for_changes,
BOOL fileWasMerged,
BOOL *fileChangedOutPtr,
ABMF_SKIP_WHY *fileSKippedReasonOutPtr
);
static BOOL file_changed_since_last_log(GenLog genLog, STRING fileName);
static time_t get_file_mod_time(STRING fileName);
static BOOL source_files_equal(FILE *file1, FILE *file2);
static int move_file(STRING existingName, STRING newName, BOOL force);
static int replace_file(STRING fileName, File fromFile, BOOL rewindFiles);
static time_t mkgmtime(struct tm * tloc);
static int touch_file(STRING fileName);
static int update_msg_set(MsgFile msgFile, ABObj obj);
/*
* Various types of progress messages
*/
#define print_comparing_message(_fname) \
(print_progress_message(3, "comparing", _fname))
#define print_processing_message(_fname) \
(print_progress_message(2, "processing", _fname))
#define print_merging_message(_fname) \
(print_progress_message(2, "merging", _fname))
#define print_writing_message(_fname) \
{STRING progress_msg; \
progress_msg=XtNewString(catgets(Dtb_project_catd, 1, 52, "writing"));\
print_progress_message(1,progress_msg,_fname);\
XtFree(progress_msg);}
/*************************************************************************
** **
** Data **
** **
**************************************************************************/
extern BOOL freshenUnchangedFiles;
static BOOL appResFileComplete = FALSE;
/*************************************************************************
** **
** Function Definitions **
** **
**************************************************************************/
/*
* Write all the code that the user wants written.
*/
int
abmf_generate_code(
ABObj project,
ABMF_CGEN_RESTRICTION restriction,
BOOL merge_files,
ABMF_I18N_METHOD i18n_method_in,
BOOL prototype_functions_in,
AB_ARG_CLASS_FLAGS dumped_resources_in
)
{
int return_value = 0;
int iRC = 0;/* int return code */
AB_TRAVERSAL trav;
ABObj module = NULL;
char resFileName[MAX_PATH_SIZE];
BOOL writeAll = FALSE;
GenCodeInfoRec genCodeInfoRec;
GenCodeInfo genCodeInfo = &genCodeInfoRec;
GenLogRec genLogRec,
*genLog = &(genLogRec);
BOOL forceWriteProjectCFile = FALSE;
BOOL headerFileChanged = FALSE;
BOOL uiCFileChanged = FALSE;
BOOL stubsCFileChanged = FALSE;
BOOL makeFileChanged = FALSE;
BOOL msgFileChanged = FALSE;
BOOL resFileChanged = FALSE;
BOOL check_dates = FALSE;
*resFileName = 0;
abmfP_gencode_construct(&genCodeInfoRec);
log_construct(&genLogRec);
check_dates = ((restriction == ABMF_CGEN_BY_DATE) ||
(restriction == ABMF_CGEN_BY_DATE_AND_CONTENTS));
/*
* Set up GenCodeInfo data
*/
genCodeInfo->prototype_funcs = prototype_functions_in;
genCodeInfo->dumped_resources = dumped_resources_in;
genCodeInfo->i18n_method = i18n_method_in;
appResFileComplete = FALSE;
/*
* Determine if we're writing EVERYTHING
*/
if (obj_get_write_me(project))
{
writeAll = TRUE;
for (trav_open(&trav, project, AB_TRAV_MODULES);
(module = trav_next(&trav)) != NULL;)
{
if (!obj_get_write_me(module))
{
writeAll = FALSE;
break;
}
}
trav_close(&trav);
}
abmfP_tree_set_action_names(project);
/*
* Open message source file if we have i18n enabled
*/
if (genCodeInfo->i18n_method == ABMF_I18N_XPG4_API)
{
if (!abmfP_initialize_msg_file(genCodeInfo, project))
{
util_dprintf(0, "Cannot generate message source file: %s\n",
genCodeInfo->msg_src_file_name);
return_value = -1;
goto epilogue;
}
}
/*
* Write out the modules
*/
for (trav_open(&trav, project, AB_TRAV_MODULES);
(module = trav_next(&trav)) != NULL;)
{
if (!obj_get_write_me(module)
|| !obj_is_defined(module))
{
continue;
}
/*
* Open resource file (if necessary)
*/
abmfP_get_intermediate_res_file_name(
module, resFileName, MAX_PATH_SIZE);
if (genCodeInfo->dumped_resources == AB_ARG_CLASS_FLAGS_NONE)
{
unlink(resFileName);
}
else
{
abmfP_get_intermediate_res_file_name(
module, resFileName, MAX_PATH_SIZE);
genCodeInfo->resource_file =
abmfP_res_file_open(resFileName, resFileName, module, TRUE);
if (genCodeInfo->resource_file == NULL)
{
exit(1);
}
}
iRC = write_module_files(genCodeInfo, genLog,
module, merge_files, check_dates, resFileName,
&headerFileChanged, &uiCFileChanged, &stubsCFileChanged);
return_if_err(iRC, iRC);
if (headerFileChanged)
{
forceWriteProjectCFile = TRUE;
}
abmfP_res_file_close(genCodeInfo->resource_file);
}
trav_close(&trav);
if (return_value < 0)
{
goto epilogue;
}
/*
* Write project/main files.
*/
if (obj_get_write_me(project))
{
abmfP_get_intermediate_res_file_name(
project, resFileName, MAX_PATH_SIZE);
if (genCodeInfo->dumped_resources == AB_ARG_CLASS_FLAGS_NONE)
{
unlink(resFileName);
}
else
{
abmfP_get_intermediate_res_file_name(
project, resFileName, MAX_PATH_SIZE);
genCodeInfo->resource_file =
abmfP_res_file_open(resFileName, resFileName, project, TRUE);
if (genCodeInfo->resource_file == NULL)
{
exit(1);
}
}
iRC = write_project_files(genCodeInfo, genLog, project,
merge_files, check_dates, forceWriteProjectCFile,
&headerFileChanged, &stubsCFileChanged);
return_if_err(iRC, iRC);
abmfP_res_file_close(genCodeInfo->resource_file);
}
/*
* Build the application resource file
*/
iRC = write_app_resource_file(
genCodeInfo,
genLog,
project,
merge_files,
check_dates,
&resFileChanged);
return_if_err(iRC,iRC);
/*
* Write more project files
*/
if (obj_get_write_me(project))
{
iRC = write_utils_files(genCodeInfo, genLog, project, check_dates,
&headerFileChanged, &stubsCFileChanged);
return_if_err(iRC, iRC);
iRC = write_make_file(genCodeInfo, genLog,
project, merge_files, check_dates, &makeFileChanged);
return_if_err(iRC, iRC);
}
iRC = write_msg_file( genCodeInfo,
genLog,
project,
check_dates,
&msgFileChanged);
return_if_err(iRC, iRC);
if (!appResFileComplete)
{
/*
* REMIND: This should be a warning message the user sees. We
* need permission to change the msg catalog
*/
if (debugging())
{
util_dprintf(1,
"Warning: Application resource file incomplete, because some modules\n");
util_dprintf(1,
" have not yet been processed.\n");
}
}
epilogue:
if (return_value < 0)
{
util_puts_err("\n");
util_puts_err(catgets(Dtb_project_catd, 1, 53,
"** Aborting due to errors **\n"));
}
abmfP_res_file_close(genCodeInfo->resource_file); /* NULL OK */
log_destruct(&genLogRec);
abmfP_gencode_destruct(&genCodeInfoRec);
return return_value;
}
/*
* Write code for one module
*/
static int
write_module_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj module,
BOOL merge_files,
BOOL check_dates,
STRING resFileName,
BOOL * headerFileChangedPtr,
BOOL * uiCFileChangedPtr,
BOOL * stubsCFileChangedPtr
)
{
#define headerFileChanged (*headerFileChangedPtr)
#define uiCFileChanged (*uiCFileChangedPtr)
#define stubsCFileChanged (*stubsCFileChangedPtr)
#define codeFile (genCodeInfo->code_file)
int return_value = 0;
int iRC = 0;/* int return code */
STRING errmsg = NULL;
char uiHeaderFileName[MAX_PATH_SIZE];
char uiHeaderDefineName[MAX_PATH_SIZE];
char uiCFileName[MAX_PATH_SIZE];
char stubsFileName[MAX_PATH_SIZE];
char stubsBakFileName[MAX_PATH_SIZE];
BOOL needStubsMerge = FALSE;
STRING curFileName = NULL;
BOOL curFileSkipped = FALSE;
ABMF_SKIP_WHY curFileSkipReason = ABMF_SKIP_UNDEF;
*uiHeaderFileName = 0;
*uiHeaderDefineName = 0;
*uiCFileName = 0;
*stubsFileName = 0;
*stubsBakFileName = 0;
headerFileChanged = FALSE;
uiCFileChanged = FALSE;
stubsCFileChanged = FALSE;
if (!ab_c_ident_is_ok(obj_get_name(module)))
{
char *prog_name_string = util_get_program_name();
char *module_name_string = obj_get_name(module);
fprintf(stderr, catgets(Dtb_project_catd, 1, 54,
"%s: module filename must contain only letters\n"),
prog_name_string);
fprintf(stderr, catgets(Dtb_project_catd, 1, 55,
"and digits. %s will generate C variables based\n"),
prog_name_string);
fprintf(stderr, catgets(Dtb_project_catd, 1, 56,
"on the module filename. Please rename %s\n"), module_name_string);
exit(1);
}
/*
* Determine the file names
*/
sprintf(uiCFileName, "%s_ui.c", obj_get_name(module));
sprintf(uiHeaderFileName, "%s_ui.h", obj_get_name(module));
strcpy(uiHeaderDefineName,
abmfP_get_define_from_file_name(uiHeaderFileName));
sprintf(stubsFileName, "%s_stubs.c", obj_get_name(module));
sprintf(stubsBakFileName, "%s.BAK", stubsFileName);
/*
* ***** WRITE UI HEADER FILE *****
*/
curFileName = uiHeaderFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
if (!should_write_file(
uiHeaderFileName, obj_get_file(module), genLog, FALSE, check_dates))
{
curFileSkipReason = ABMF_SKIP_UP_TO_DATE;
}
else
{
print_processing_message(uiHeaderFileName);
if ((errmsg = abio_open_output(NULL, &codeFile)) != NULL)
{
util_printf_err("%s\n", errmsg);
return_code(ERR_OPEN);
}
iRC = abmfP_write_ui_header_file(genCodeInfo,
module,
uiHeaderFileName,
uiHeaderDefineName);
return_if_err(iRC,iRC);
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
FALSE,
&headerFileChanged,
&curFileSkipReason);
return_if_err(iRC,iRC);
abio_close_output(codeFile);
} /* date check */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
/*
* ***** WRITE UI.C FILE *****
*/
curFileName = uiCFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
if (!should_write_file(uiCFileName, obj_get_file(module), genLog,
FALSE, check_dates))
{
curFileSkipReason = ABMF_SKIP_UP_TO_DATE;
}
else
{
print_processing_message(curFileName);
if (!util_be_silent())
{
if ((genCodeInfo->resource_file != NULL) && (resFileName != NULL))
{
char *prog_name_string = util_get_program_name();
fprintf(stderr, catgets(Dtb_project_catd, 1, 57,
"%s: writing resources for %s into %s\n"),
prog_name_string,
uiCFileName,
resFileName);
}
}
if ((errmsg = abio_open_output(NULL, &codeFile)) != NULL)
{
util_printf_err("%s\n", errmsg);
return_code(ERR_OPEN);
}
iRC = abmfP_write_ui_c_file(genCodeInfo, uiCFileName, module);
return_if_err(iRC, iRC);
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
FALSE,
&uiCFileChanged,
&curFileSkipReason);
return_if_err(iRC, iRC);
abio_close_output(codeFile);
} /* date check */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
/***** WRITE STUBS.C FILE *****/
/*
* If the stubs file is already present, make a backup of it.
*/
curFileName = stubsFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
if (!should_write_file(stubsFileName, obj_get_file(module), genLog,
merge_files, check_dates))
{
curFileSkipReason = ABMF_SKIP_UP_TO_DATE;
}
else
{
needStubsMerge = merge_files;
if (!util_file_exists(stubsFileName))
{
needStubsMerge = FALSE;
}
if (curFileSkipReason == ABMF_SKIP_UNDEF)
{
/*
* Open the output (temp) file.
*/
File deltaFile = NULL;
errmsg = abio_open_output(NULL, &codeFile); /* tmp file */
if (errmsg != NULL)
{
util_printf_err("%s\n", errmsg);
return_code(ERR_OPEN);
}
/*
* Write the output file
*/
print_processing_message(stubsFileName);
iRC = abmfP_write_stubs_c_file(genCodeInfo, stubsFileName, module);
return_if_err(iRC, iRC);
/*
* Merge the file
*/
iRC = check_and_merge_c_file(
needStubsMerge,
&codeFile,
curFileName,
&curFileSkipReason,
&deltaFile);
return_if_err(iRC,iRC);
/*
* Replace the file with the new version
*/
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
needStubsMerge,
&stubsCFileChanged,
&curFileSkipReason);
return_if_err(iRC,iRC);
if ((stubsCFileChanged) && (deltaFile != NULL))
{
char deltaFileName[MAXPATHLEN];
sprintf(deltaFileName, "%s.delta", stubsFileName);
replace_file(deltaFileName, deltaFile, TRUE);
}
util_fclose(codeFile);
util_fclose(deltaFile);
} /* !curFileSkipped */
} /* date check */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
epilogue:
abio_close_output(codeFile);
return return_value;
#undef headerFileChanged
#undef uiCFileChanged
#undef uiStubsFileChanged
#undef codeFile
}
/*
* Write the code for the main files of a project (project.h, project.c).
*/
static int
write_project_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL merge_files,
BOOL check_dates,
BOOL force_write_c_file,
BOOL * headerFileChangedPtr,
BOOL * stubsCFileChangedPtr
)
{
#define headerFileChanged (*headerFileChangedPtr)
#define stubsCFileChanged (*stubsCFileChangedPtr)
#define codeFile (genCodeInfo->code_file)
int return_value = 0;
int iRC = 0;/* int return code */
STRING errmsg = NULL;
BOOL needHeaderMerge = FALSE;
BOOL needStubsMerge = FALSE;
char headerFileName[MAX_PATH_SIZE];
char headerDefineName[MAX_PATH_SIZE];
char headerBakFileName[MAX_PATH_SIZE];
char stubsFileName[MAX_PATH_SIZE];
char stubsBakFileName[MAX_PATH_SIZE];
STRING curFileName = NULL;
ABMF_SKIP_WHY curFileSkipReason = ABMF_SKIP_UNDEF;
File orgFile = NULL;
File mergedFile = NULL;
File deltaFile = NULL;
headerFileChanged = FALSE;
stubsCFileChanged = FALSE;
sprintf(headerFileName, "%s.h", obj_get_name(project));
strcpy(headerDefineName, abmfP_get_define_from_file_name(headerFileName));
sprintf(headerBakFileName, "%s.BAK", headerFileName);
sprintf(stubsFileName, "%s.c", obj_get_name(project));
sprintf(stubsBakFileName, "%s.BAK", stubsFileName);
/***** PROJECT HEADER FILE *****/
curFileName = headerFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
needHeaderMerge = (merge_files && util_file_exists(curFileName));
if (!should_write_file(
headerFileName, obj_get_file(project), genLog,
needHeaderMerge, check_dates))
{
curFileSkipReason = ABMF_SKIP_UP_TO_DATE;
}
else
{
/*
* Write the beastie
*/
print_processing_message(headerFileName);
if ((errmsg = abio_open_output(NULL, &codeFile)) != NULL)
{
util_printf_err("%s\n", errmsg);
return ERR_OPEN;
}
iRC = abmfP_write_project_header_file(
genCodeInfo,
project,
headerFileName);
return_if_err(iRC, iRC);
/*
* Merge the file
*/
iRC = check_and_merge_c_file(
needHeaderMerge,
&codeFile,
curFileName,
&curFileSkipReason,
&deltaFile);
return_if_err(iRC,iRC);
/*
* Replace the old version with the new
*/
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
needHeaderMerge,
&headerFileChanged,
&curFileSkipReason);
return_if_err(iRC, iRC);
} /* compare_file_times */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
abio_close_output(codeFile);
util_fclose(orgFile);
util_fclose(mergedFile);
util_fclose(deltaFile);
/*
* ***** PROJECT .C *****
*/
curFileName = stubsFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
needStubsMerge = (merge_files && util_file_exists(curFileName));
if (!(force_write_c_file ||
should_write_file(stubsFileName, obj_get_file(project),
genLog, merge_files, check_dates)))
{
curFileSkipReason = ABMF_SKIP_UP_TO_DATE;
}
else
{
if (util_be_verbose())
{
if (check_dates && force_write_c_file)
{
util_printf(catgets(Dtb_project_catd, 1, 58,
"generating %s because of changes in modules\n"),
stubsFileName);
}
}
print_processing_message(stubsFileName);
errmsg = abio_open_output(NULL, &codeFile);
if (errmsg != NULL)
{
return_value = ERR_OPEN;
util_printf_err("%s\n", errmsg);
goto epilogue;
}
iRC = abmfP_write_project_c_file(
genCodeInfo, stubsFileName, needStubsMerge, project);
return_if_err(iRC, iRC);
/*
* Merge the file
*/
iRC = check_and_merge_c_file(
needStubsMerge,
&codeFile,
curFileName,
&curFileSkipReason,
&deltaFile);
return_if_err(iRC,iRC);
/*
* Replace the old version with the new
*/
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
needStubsMerge,
&stubsCFileChanged,
&curFileSkipReason);
return_if_err(iRC, iRC);
} /* compare_file_times */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
epilogue:
abio_close_output(codeFile);
util_fclose(orgFile);
util_fclose(mergedFile);
util_fclose(deltaFile);
return return_value;
#undef headerFileChanged
#undef stubsCFileChanged
#undef codeFile
}
static int
write_make_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL merge_files,
BOOL check_dates,
BOOL *makeFileChangedPtr
)
{
#define makeFileChanged (*makeFileChangedPtr)
#define makeFile (genCodeInfo->code_file)
int return_value = 0;
int rc = 0;
STRING errmsg = NULL;
int intOSMin = ((int) AB_OS_UNDEF) + 1;
int intOSMax = ((int) AB_OS_TYPE_NUM_VALUES) - 1;
AB_OS_TYPE actualOSType = util_get_os_type();
AB_OS_TYPE curOSType = AB_OS_UNDEF;
STRING curOSTypeString = NULL;
char curMakeFileName[256] = "";
BOOL curMakeFileIsActual = FALSE;
BOOL writeCurMakeFile = FALSE;
STRING actualMakeFileName = "Makefile";
BOOL actualMakeFileModified = FALSE;
BOOL actualMakeFileIsOurs = FALSE;
int i = 0;
merge_files = merge_files; /* ignored, for now */
check_dates = check_dates;
makeFileChanged = FALSE;
/* if it's not there, we own it */
actualMakeFileIsOurs = !util_file_exists(actualMakeFileName);
for (i = intOSMin; i <= intOSMax; ++i)
{
writeCurMakeFile = TRUE;
curMakeFileIsActual = FALSE;
curOSType = (AB_OS_TYPE) i;
curOSTypeString = util_os_type_to_ident(curOSType);
if (curOSTypeString == NULL)
{
continue;
}
sprintf(curMakeFileName, "%s.%s", actualMakeFileName, curOSTypeString);
if (util_paths_are_same_file(curMakeFileName, actualMakeFileName))
{
curMakeFileIsActual = TRUE;
actualMakeFileIsOurs = TRUE;
}
if (file_changed_since_last_log(genLog, curMakeFileName))
{
writeCurMakeFile = FALSE;
if (curMakeFileIsActual)
{
actualMakeFileModified = TRUE;
}
if (util_be_verbose())
{
char *prog_name_string = util_get_program_name();
fprintf(stderr, catgets(Dtb_project_catd, 1, 59,
"%s: Skipping user-defined %s\n"),
prog_name_string, curMakeFileName);
}
}
if (writeCurMakeFile)
{
print_writing_message(curMakeFileName);
if ((errmsg = abio_open_output(curMakeFileName, &makeFile)) != NULL)
{
util_printf_err("%s\n", errmsg);
goto epilogue;
}
abmfP_write_make_file(genCodeInfo, project, curOSType, FALSE);
abio_close_output(makeFile);
makeFileChanged = TRUE;
log_add_entry(genLog, curMakeFileName, -1, FALSE);
}
} /* for osType */
if (actualOSType == AB_OS_UNDEF)
{
util_printf_err(catgets(Dtb_project_catd, 1, 60,
"Could not determine OS type of this machine\n"));
goto epilogue;
}
sprintf(curMakeFileName, "%s.%s",
actualMakeFileName, util_os_type_to_ident(actualOSType));
if (! (actualMakeFileIsOurs && (!actualMakeFileModified)) )
{
/* The user has modified this file since we wrote it last */
if (util_be_verbose())
{
util_printf(catgets(Dtb_project_catd, 1, 61,
"Skipping user-defined %s\n"), actualMakeFileName);
}
}
else
{
if (!util_be_silent())
{
util_printf(catgets(Dtb_project_catd, 1, 62,
"linking %s => %s\n"), curMakeFileName, actualMakeFileName);
}
rc = unlink(actualMakeFileName);
if (util_file_exists(actualMakeFileName))
{
util_printf_err(catgets(Dtb_project_catd, 1, 63,
"Could not remove %s\n"), actualMakeFileName);
return_code(-1);
}
rc = link(curMakeFileName, actualMakeFileName);
if (rc != 0)
{
util_printf_err(catgets(Dtb_project_catd, 1, 64,
"Could not create link to %s\n"), curMakeFileName);
return_code(-1);
}
log_add_entry(genLog, actualMakeFileName, -1, FALSE);
}
epilogue:
abio_close_output(makeFile);
return return_value;
#undef makeFileChanged
#undef makeFile
}
static int
write_utils_files(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL check_dates,
BOOL *headerFileChangedPtr,
BOOL *CFileChangedPtr
)
{
#define headerFileChanged (*headerFileChangedPtr)
#define CFileChanged (*CFileChangedPtr)
#define codeFile (genCodeInfo->code_file)
int return_value = 0;
int iRC = 0; /* int return code */
STRING curFileName = NULL;
ABMF_SKIP_WHY curFileSkipReason = ABMF_SKIP_UNDEF;
STRING utilHFileName = abmfP_get_utils_header_file_name(project);
STRING utilCFileName = abmfP_get_utils_c_file_name(project);
STRING errmsg = NULL;
/***** UTILITY HEADER FILE *****/
curFileName = utilHFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
/*
* Write the beastie
*/
print_processing_message(curFileName);
if ((errmsg = abio_open_output(NULL, &codeFile)) != NULL)
{
util_printf_err("%s\n", errmsg);
return ERR_OPEN;
}
iRC = abmfP_write_utils_header_file(genCodeInfo, curFileName, project);
return_if_err(iRC, iRC);
/*
* Replace the old version with the new
*/
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
FALSE,
&headerFileChanged,
&curFileSkipReason);
return_if_err(iRC, iRC);
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
abio_close_output(codeFile);
/*
* ***** PROJECT .C *****
*/
curFileName = utilCFileName;
curFileSkipReason = ABMF_SKIP_UNDEF;
{
print_processing_message(curFileName);
errmsg = abio_open_output(NULL, &codeFile);
if (errmsg != NULL)
{
return_value = ERR_OPEN;
util_printf_err("%s\n", errmsg);
goto epilogue;
}
iRC = abmfP_write_utils_c_file(genCodeInfo, curFileName, project);
return_if_err(iRC, iRC);
/*
* Replace the old version with the new
*/
iRC = check_and_replace_file(
curFileName,
genLog,
codeFile,
check_dates,
FALSE,
&CFileChanged,
&curFileSkipReason);
return_if_err(iRC, iRC);
} /* compare_file_times */
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
epilogue:
abio_close_output(codeFile);
return return_value;
#undef headerFileChanged
#undef CFileChanged
#undef codeFile
}
#ifdef BOGUS
{
#define codeFile (genCodeInfo->code_file)
int return_value = 0;
int rc = 0; /* return code */
STRING errmsg = NULL;
STRING utilHFileName = NULL;
STRING utilCFileName = NULL;
genLog = genLog; /* avoid warning */
utilHFileName = abmfP_get_utils_header_file_name(project);
utilCFileName = abmfP_get_utils_c_file_name(project);
errmsg = abio_open_output(utilHFileName, &codeFile);
if (errmsg != NULL)
{
util_printf_err("%s\n", errmsg);
}
print_processing_message(utilHFileName);
rc = abmfP_write_utils_header_file(genCodeInfo, utilHFileName, project);
return_if_err(rc,rc);
abio_close_output(codeFile);
errmsg = abio_open_output(utilCFileName, &codeFile);
if (errmsg != NULL)
{
util_printf_err("%s\n", errmsg);
}
print_writing_message(utilCFileName);
rc = abmfP_write_utils_c_file(genCodeInfo, utilCFileName, project);
return_if_err(rc,rc);
abio_close_output(codeFile);
epilogue:
abio_close_output(codeFile);
return return_value;
#undef codeFile
}
#endif /* BOGUS */
static int
write_app_resource_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL merge_files,
BOOL check_dates,
BOOL *fileChangedOut
)
{
int return_value = 0;
int rc = 0; /* return code */
File newResFile = NULL;
char appResFileName[MAXPATHLEN+1];
STRING curFileName = NULL;
ABMF_SKIP_WHY curFileSkipReason = ABMF_SKIP_UNDEF;
*appResFileName = 0;
/*
* Write new vanilla resource file
*/
abmfP_get_app_res_file_name(project, appResFileName, MAXPATHLEN+1);
print_processing_message(appResFileName);
newResFile = abmfP_res_file_open(NULL, appResFileName, project, FALSE);
curFileName = appResFileName;
abio_printf(newResFile,
"! All CDE applications should include the standard Dt resource file\n"
"#include \"Dt\"\n"
"\n");
rc = abmfP_write_app_res_file(newResFile, project, appResFileName);
if (rc < 0)
{
/* error */
}
else if (rc == 0)
{
appResFileComplete = TRUE;
}
else if (rc == 1)
{
/* file built successfully, but is incomplete */
if (!util_be_silent())
{
/*
* We don't want to merge this file. Its pointless, since it's
* incomplete, and it may make us overwrite an existing
* complete resource file.
*/
abmfP_res_file_close(newResFile);
goto epilogue;
}
}
/*
* Merge the files, if necessary
*/
if (merge_files)
{
File oldResFile = util_fopen_locked(appResFileName, "r");
File mergedResFile = NULL;
int rc = 0;
if (oldResFile != NULL)
{
print_merging_message(curFileName);
abmfP_res_file_merge(newResFile, oldResFile, &mergedResFile);
assert(rc >= 0);
if (mergedResFile != NULL)
{
util_fclose(newResFile);
newResFile = mergedResFile;
mergedResFile = NULL;
}
}
}
/*
* See if anything changed
*/
rc = check_and_replace_file(
appResFileName,
genLog,
newResFile,
check_dates,
merge_files,
fileChangedOut,
&curFileSkipReason);
return_if_err(rc,rc);
if (curFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, curFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
epilogue:
return return_value;
}
static int
write_msg_file(
GenCodeInfo genCodeInfo,
GenLog genLog,
ABObj project,
BOOL check_for_changes,
BOOL *msgFileChangedOutPtr
)
{
int return_value = 0;
int rc = 0; /* return code */
File newMsgFile = NULL;
ABMF_SKIP_WHY msgFileSkipReason = ABMF_SKIP_UNDEF;
STRING curFileName = genCodeInfo->msg_src_file_name;
AB_TRAVERSAL trav;
ABObj module = NULL;
update_msg_set(genCodeInfo->msg_file_obj, project);
for (trav_open(&trav, project, AB_TRAV_MODULES);
(module = trav_next(&trav)) != NULL; )
{
update_msg_set(genCodeInfo->msg_file_obj, module);
}
trav_close(&trav);
/*
* Build the message catalog
*/
if (genCodeInfo->i18n_method == ABMF_I18N_XPG4_API)
{
print_processing_message(curFileName);
rc = MsgFile_save(genCodeInfo->msg_file_obj, &newMsgFile);
return_if_err(rc,rc);
rc = check_and_replace_file(
curFileName,
genLog,
newMsgFile,
check_for_changes,
TRUE,
msgFileChangedOutPtr,
&msgFileSkipReason);
return_if_err(rc,rc);
}
if (msgFileSkipReason != ABMF_SKIP_UNDEF)
{
print_skipping_message(curFileName, msgFileSkipReason);
if (freshenUnchangedFiles)
{
touch_file(curFileName);
}
}
epilogue:
util_fclose(newMsgFile);
return return_value;
}
/*
* Ensures that all the messages for the given object have been
* created, if the object is being generated.
*
* For objects that are not being code-generated, makes sure that no
* messages will be deleted.
*/
static int
update_msg_set(MsgFile msgFile, ABObj obj)
{
#define do_string(_s) \
if ((string = (_s)) != NULL) \
{ \
MsgSet_sure_find_msg(msgSet, string); \
}
int return_value = 0;
STRING string = NULL;
MsgSet msgSet = NULL;
ABObj stringObj = NULL;
BOOL objIsProject = obj_is_project(obj);
AB_TRAVERSAL trav;
assert(obj_is_module(obj) || obj_is_project(obj));
msgSet = MsgFile_obj_sure_find_msg_set(msgFile, obj);
if (msgSet == NULL)
{
return ERR_INTERNAL;
}
if (obj_get_write_me(obj))
{
MsgSet_set_is_referenced(msgSet, TRUE);
MsgSet_set_allow_msg_delete(msgSet, TRUE);
for (trav_open(&trav, obj, AB_TRAV_ALL);
(stringObj = trav_next(&trav)) != NULL; )
{
if (objIsProject && (obj_get_module(stringObj) != NULL))
{
continue;
}
do_string(obj_get_arg_string(stringObj));
do_string(obj_get_menu_title(stringObj));
do_string(obj_get_ok_label(stringObj));
do_string(obj_get_msg_string(stringObj));
do_string(obj_get_action1_label(stringObj));
do_string(obj_get_action2_label(stringObj));
do_string(obj_get_initial_value_string(stringObj));
do_string(obj_get_icon_label(stringObj));
do_string(obj_get_label(stringObj));
}
trav_close(&trav);
}
else
{
MsgSet_set_allow_msg_delete(msgSet, FALSE);
}
return return_value;
#undef do_string
}
/*
* Returns TRUE if the output file should be written
*/
static int
should_write_file(
STRING output_file,
STRING input_file,
GenLog genLog,
BOOL merge_files,
BOOL check_times
)
{
static BOOL initialized = FALSE;
static time_t exeDate = (time_t)-1;
GenLogEntry logEntry = NULL;
struct stat fileInfo;
/*
return TRUE;
* This is supposed to check to see generation parameters have changed
* that should cause the file to be regenerated. Unfortunately, it's gotten
* out-of-date and does not catch all cases.
*
* Don't delete this, it can be revamped and re-used later.
*/
/* #ifdef BOGUS save this - we will need it again */
if (!initialized)
{
char exePath[MAX_PATH_SIZE];
STRING exeDir = NULL;
STRING exeFile = NULL;
*exePath = 0;
initialized = TRUE;
exeDir = dtb_get_exe_dir();
exeFile = util_get_program_name();
if (exeDir != NULL)
{
strcpy(exePath, exeDir);
}
if (exeFile != NULL)
{
strcat(exePath, "/");
strcat(exePath, exeFile);
}
exeDate = (time_t)0;
if (stat(exePath, &fileInfo) == 0)
{
exeDate = fileInfo.st_mtime;
}
}
if (!check_times)
{
return TRUE;
}
/*
* See if the generation parameters have changed.
*/
logEntry = log_find_entry_by_name(genLog, output_file);
if ((logEntry != NULL) && (util_xor(logEntry->merged, merge_files)))
{
return TRUE;
}
/*
* See if the code generator executable is newer than this file
*/
if (stat(output_file, &fileInfo) == 0)
{
if (exeDate > fileInfo.st_mtime)
{
return TRUE;
}
}
/*
* Check input vs. output timestamps
*/
return (compare_file_times(input_file, output_file, genLog) > 0);
/*#endif BOGUS */
}
static int
check_and_merge_c_file(
BOOL needMerge,
File *codeFileInOut,
STRING codeFileName,
ABMF_SKIP_WHY *fileSkipReasonInOut,
File *deltaFileOut
)
{
int return_value = 0;
int rc = 0; /* return code */
File codeFile = *codeFileInOut;
File mergedFile = NULL;
File deltaFile = NULL;
File *deltaFilePtr = NULL;
File orgFile = NULL;
#ifdef DEBUG
if (debugging())
{
deltaFilePtr = &deltaFile; /* delta file is primarily debugging */
}
#endif /* DEBUG */
*deltaFileOut = NULL;
if ( ((*fileSkipReasonInOut) != ABMF_SKIP_UNDEF)
|| (!needMerge) )
{
return 0;
}
print_merging_message(codeFileName);
orgFile = util_fopen_locked(codeFileName, "r");
if (orgFile == NULL)
{
*fileSkipReasonInOut = ABMF_SKIP_ERR_OCCURRED;
util_printf_err("%s: %s\n", codeFileName, strerror(errno));
return_code(-1);
}
rewind(codeFile);
rewind(orgFile);
rc = abmfP_merge_c_files(
codeFile, "new file",
orgFile, codeFileName,
&mergedFile, deltaFilePtr);
if (rc < 0)
{
*fileSkipReasonInOut = ABMF_SKIP_ERR_OCCURRED;
return_code(-1);
}
if (mergedFile == NULL)
{
*fileSkipReasonInOut = ABMF_SKIP_NO_CHANGES;
}
else
{
/* changes were detected and merged */
util_fclose(codeFile);
codeFile = mergedFile; mergedFile = NULL;
}
epilogue:
util_fclose(orgFile);
*deltaFileOut = deltaFile;
*codeFileInOut = codeFile;
return return_value;
}
/*
* genLog may be NULL
*/
static int
check_and_replace_file(
STRING fileName,
GenLog genLog,
File newFile,
BOOL check_for_changes,
BOOL fileWasMerged,
BOOL *fileChangedOutPtr,
ABMF_SKIP_WHY *fileSkippedReasonOutPtr
)
{
#define fileSkippedReasonOut (*fileSkippedReasonOutPtr)
int return_value = 0;
int rc = 0; /* return code */
BOOL fileChanged = FALSE;
BOOL updateLog = FALSE;
fileSkippedReasonOut = ABMF_SKIP_UNDEF;
if (check_for_changes && util_file_exists(fileName))
{
File orgFile = util_fopen_locked(fileName, "r");
if (orgFile == NULL)
{
fileSkippedReasonOut = ABMF_SKIP_ERR_OCCURRED;
util_printf_err(catgets(Dtb_project_catd, 1, 65, "%s: %s\n"),
fileName, strerror(errno));
return_code(-1);
}
print_comparing_message(fileName);
fileChanged = !source_files_equal(newFile, orgFile);
util_fclose(orgFile);
}
else
{
fileChanged = TRUE;
}
if (fileChanged)
{
print_writing_message(fileName);
/*
* Check write permission on file
*/
if (util_file_exists(fileName))
{
File file = util_fopen_locked(fileName, "a+");
if (file == NULL)
{
fileSkippedReasonOut = ABMF_SKIP_ERR_OCCURRED;
util_printf_err(catgets(Dtb_project_catd, 1, 65, "%s: %s\n"),
fileName, strerror(errno));
return_code(ERR_OPEN);
}
util_fclose(file);
}
/*
* Replace the old with the new
*/
rc = replace_file(fileName, newFile, TRUE);
if (rc < 0)
{
fileChanged = FALSE;
fileSkippedReasonOut = ABMF_SKIP_ERR_OCCURRED;
return_code(rc);
}
else
{
/* successful creation of new file */
updateLog = TRUE;
}
}
else
{
/* no changes - it's up-to-date */
fileSkippedReasonOut = ABMF_SKIP_NO_CHANGES;
if (freshenUnchangedFiles)
{
touch_file(fileName); /* update the timestamp */
}
updateLog = TRUE;
}
epilogue:
if (updateLog && (genLog != NULL))
{
log_add_entry(genLog, fileName, -1, fileWasMerged);
}
*fileChangedOutPtr = fileChanged;
return return_value;
#undef fileSkippedReasonOut
} /* check_and_replace_file */
static int
move_file(STRING fileName, STRING newFileName, BOOL force)
{
int rc = 0;
if (force)
{
unlink(newFileName);
}
rc = link(fileName, newFileName);
if (rc != 0)
{
return -1;
}
rc = unlink(fileName);
if (rc != 0)
{
return -1;
}
return 0;
}
static int
replace_file(STRING fileName, File fromFile, BOOL rewindFiles)
{
int c;
char bakFileName[MAXPATHLEN];
File toFile = NULL;
if (rewindFiles)
{
rewind(fromFile);
}
if (util_file_exists(fileName))
{
sprintf(bakFileName, "%s.BAK", fileName);
print_backing_up_message(fileName, bakFileName);
move_file(fileName, bakFileName, TRUE);
}
toFile = util_fopen_locked(fileName, "w");
if (toFile == NULL)
{
util_printf_err(catgets(Dtb_project_catd, 1, 65, "%s: %s\n"),
fileName, strerror(errno));
return ERR_OPEN;
}
while ((c = fgetc(fromFile)) != EOF)
{
fputc(c, toFile);
}
util_fclose(toFile);
return 0;
}
static int
print_progress_message(int verbosity, STRING message, STRING fileName)
{
if (util_get_verbosity() >= verbosity)
{
util_printf_err(catgets(Dtb_project_catd, 1, 67, "%s %s\n"),
message, fileName);
}
return 0;
}
static int
print_skipping_message(STRING fileName, ABMF_SKIP_WHY why)
{
if (util_be_verbose())
{
switch (why)
{
case ABMF_SKIP_NO_CHANGES:
util_printf(catgets(Dtb_project_catd, 1, 68,
"skipping (no changes) %s\n"), fileName);
break;
case ABMF_SKIP_UP_TO_DATE:
util_printf(catgets(Dtb_project_catd, 1, 69,
"skipping (up-to-date) %s\n"), fileName);
break;
case ABMF_SKIP_ERR_OCCURRED:
util_printf(catgets(Dtb_project_catd, 1, 70,
"skipping due to errors %s\n"), fileName);
break;
}
}
return 0;
}
static int
print_backing_up_message(STRING fileFromName, STRING fileToName)
{
if (util_be_verbose())
{
util_printf(catgets(Dtb_project_catd, 1, 71,
"saving previous %s to %s\n"), fileFromName, fileToName);
}
return 0;
}
/*
* Returns: < 0 if file1 < file2 (file1 is older) 0 if file1 == file2 (files
* have same mod. time) > 0 if file1 > file2 (file1 is newer)
*/
static int
compare_file_times(STRING input_file, STRING output_file, GenLog genLog)
{
int return_value = 0;
GenLogEntry logEntry = log_find_entry_by_name(genLog, output_file);
struct stat fileInfo;
if (logEntry == NULL)
{
/* we don't have any record of output file, so input file is newer */
return 1;
}
if (!util_file_exists(output_file))
{
/* output file doesn't exit - input file is newer */
return 1;
}
if (stat(input_file, &fileInfo) != 0)
{
/* the input file doesn't exist? return 1 to force the issue */
return 1;
}
return_value = 0;
if (fileInfo.st_mtime > logEntry->mod_time)
{
return_value = 1;
}
else if (fileInfo.st_mtime < logEntry->mod_time)
{
return_value = -1;
}
return return_value;
}
/*
* See if the user has fiddled with this file
*/
static BOOL
file_changed_since_last_log(GenLog genLog, STRING fileName)
{
BOOL fileWasModified = FALSE;
GenLogEntry logEntry = NULL;
time_t lastLogWriteTime = 0;
logEntry = log_find_entry_by_name(genLog, fileName);
lastLogWriteTime = (logEntry == NULL? 0: logEntry->mod_time);
fileWasModified = (get_file_mod_time(fileName) > lastLogWriteTime);
return fileWasModified;
}
/*
* Returns (time_t)-1 if file not found, or other error
*/
static time_t
get_file_mod_time(STRING fileName)
{
time_t fileModTime = (time_t)-1;
struct stat fileInfo;
if (stat(fileName, &fileInfo) == 0)
{
fileModTime = fileInfo.st_mtime;
}
return fileModTime;
}
/*
* POSIX gives us struct -> local time, but no struct -> UTC, so we've to to
* write our own.
*/
static time_t
mkgmtime(struct tm * localTimeStruct)
{
static long timeDiff = 0;
time_t gmTime = (time_t) - 1;
time_t curTime = 0;
time_t localTime = 0;
time_t adjustedTime = 0;
struct tm *gmTimeStruct = NULL;
int localTimeIsDST = 0;
/*
* Determine time difference between localtime and gmtime
*/
curTime = time(NULL);
localTimeIsDST = localTimeStruct->tm_isdst;
if (localTimeIsDST < 0)
{
/* we're supposed to figure out DST */
struct tm *curTimeStruct = localtime(&curTime);
localTimeIsDST = curTimeStruct->tm_isdst;
}
gmTimeStruct = gmtime(&curTime);
if (gmTimeStruct == NULL)
{
goto epilogue;
}
gmTimeStruct->tm_isdst = localTimeIsDST;
adjustedTime = mktime(gmTimeStruct);
if (adjustedTime == (time_t) - 1)
{
goto epilogue;
}
timeDiff = ((long) curTime) - ((long) adjustedTime);
/*
* Convert given time as a local time and adjust
*/
localTime = mktime(localTimeStruct);
if (localTime == (time_t) - 1)
{
goto epilogue;
}
gmTime = (time_t) (((long) localTime) + ((long) timeDiff));
epilogue:
return gmTime;
}
static int
touch_file(STRING fileName)
{
int rc = utime(fileName, NULL);
return (rc == 0? OK:ERR);
}
/*
* Modifies: current position on file1,file2
* parameters may be NULL
*/
static BOOL
source_files_equal(FILE *file1, FILE *file2)
{
BOOL filesSame = TRUE;
BOOL done = FALSE;
int file1Char = ' ';
int file2Char = ' ';
if (file1 == file2)
{
return TRUE;
}
else if ((file1 == NULL) || (file2 == NULL))
{
/* one is NULL, the other is not */
return FALSE;
}
rewind(file1);
rewind(file2);
while (!done)
{
if (file1Char != EOF)
{
file1Char = fgetc(file1);
}
if (file2Char != EOF)
{
file2Char = fgetc(file2);
}
filesSame = (file1Char == file2Char);
done = ((!filesSame) || ((file1Char == EOF) && (file2Char == EOF)));
}
return filesSame;
}
/*************************************************************************
** **
** GenLog **
** **
*************************************************************************/
static int
log_construct(GenLog log)
{
log->log_file = NULL;
log->num_entries = 0;
log->entries_size = 0;
log->entries = NULL;
return logP_read(log);
}
static int
log_destruct(GenLog log)
{
logP_write(log);
logP_release_data(log);
return 0;
}
static int
logP_release_data(GenLog log)
{
int i;
istr_destroy(log->log_file);
for (i = 0; i < log->entries_size; ++i)
{
istr_destroy(log->entries[i].file_name);
log->entries[i].mod_time = (time_t) 0;
}
log->num_entries = 0;
log->entries_size = 0;
util_free(log->entries);
return 0;
}
static GenLogEntry
log_find_entry_by_name(GenLog log, STRING file_name)
{
GenLogEntry return_value = NULL;
ISTRING istr_file_name = istr_dup_existing(file_name);
int i = 0;
if (istr_file_name == NULL)
{
return_code(NULL);
}
for (i = 0; i < log->num_entries; ++i)
{
if (istr_equal(istr_file_name, log->entries[i].file_name))
{
return_value = &(log->entries[i]);
break;
}
}
epilogue:
istr_destroy(istr_file_name);
return return_value;
}
/*
* If mod_time is (time_t)-1, the file itself is checked for modification
* time.
*/
static int
log_add_entry(GenLog log, STRING file_name, time_t mod_time, BOOL merged)
{
int return_value = 0;
GenLogEntry entry = log_find_entry_by_name(log, file_name);
int new_entries_size = 0;
int new_num_entries = 0;
GenLogEntry new_entries = NULL;
if (mod_time == (time_t)-1)
{
mod_time = get_file_mod_time(file_name);
}
if (entry != NULL)
{
entry->mod_time = mod_time;
entry->merged = merged;
return_code(0);
}
/*
* Allocate space to append
*/
new_entries_size = log->entries_size;
new_num_entries = log->num_entries + 1;
new_entries_size = util_max(new_num_entries, new_entries_size);
new_entries = (GenLogEntry) realloc(log->entries,
new_entries_size * sizeof(GenLogEntryRec));
if (new_entries == NULL)
{
return_code(ERR_NO_MEMORY);
}
log->entries = new_entries;
log->entries_size = new_entries_size;
log->num_entries = new_num_entries;
/*
* Fill in the new data
*/
entry = &(log->entries[(log->num_entries) - 1]);
entry->file_name = istr_create(file_name);
entry->mod_time = mod_time;
entry->merged = merged;
epilogue:
return return_value;
}
static int
log_dump(GenLog log)
{
#ifndef DEBUG
return 0;
#else
int i = 0;
GenLogEntry entry = NULL;
if (log == NULL)
{
util_dprintf(0, "NULL log\n");
return 0;
}
if (log->num_entries == 0)
{
util_dprintf(0, "Empty log file:%s\n", istr_string_safe(log->log_file));
return 0;
}
util_dprintf(0, "===== Gen Log (file %s) =====\n",
istr_string_safe(log->log_file));
for (i = 0; i < log->num_entries; ++i)
{
entry = &(log->entries[i]);
util_dprintf(0, "%s %ld merged:%d\n",
istr_string_safe(entry->file_name), (long) (entry->mod_time),
entry->merged);
}
util_dprintf(0, "===================\n");
return 0;
#endif /* DEBUG */
}
static int
logP_read(GenLog log)
{
int return_value = 0;
char log_file_name[MAXPATHLEN] = "";
char line_buf[MAXPATHLEN] = "";
GenLogEntry next_entry = NULL;
int i = 0;
int num_lines = 0;
File file = NULL;
char *token_start = NULL;
char *line_ptr = NULL;
char *line_tmp_ptr = NULL;
int c = 0;
int year = 0;
int month = 0;
int day = 0;
int hour = 0;
int minute = 0;
int second = 0;
struct tm time_struct;
logP_release_data(log); /* re-init */
sprintf(log_file_name, ".%s.log", util_get_program_name());
log->log_file = istr_create(log_file_name);
file = util_fopen_locked(log_file_name, "rt");
if (file == NULL)
{
return_code(0);
}
num_lines = 0;
while ((c = getc(file)) != EOF)
{
if ((c == '\n') || (c == '\r'))
{
++num_lines;
}
}
if (num_lines < 1)
{
return_code(0);
}
log->entries_size = num_lines;
log->entries = (GenLogEntry) util_malloc(
log->entries_size * sizeof(GenLogEntryRec));
if (log->entries == NULL)
{
log->entries_size = 0;
return_code(ERR_NO_MEMORY);
}
for (i = 0; i < log->entries_size; ++i)
{
next_entry = &(log->entries[i]);
next_entry->file_name = NULL;
next_entry->mod_time = 0;
next_entry->merged = FALSE;
}
rewind(file);
for (; (!feof(file)) && (log->num_entries < log->entries_size);)
{
next_entry = &(log->entries[(log->num_entries)]);
*line_buf = 0;
fgets(line_buf, MAXPATHLEN, file);
line_ptr = line_buf;
/*
* file name
*/
token_start = line_ptr;
line_ptr = strchr(line_ptr, ' ');
if (line_ptr == NULL)
{
continue; /* error - skip this one */
}
*line_ptr = 0;
if (strlen(token_start) <= (size_t) 0)
{
continue; /* an error - skip this entry */
}
next_entry->file_name = istr_create(token_start);
*line_ptr = ' ';
/*
* Generation time
*/
year = logP_get_int_from_string(&line_ptr);
month = logP_get_int_from_string(&line_ptr);
day = logP_get_int_from_string(&line_ptr);
hour = logP_get_int_from_string(&line_ptr);
minute = logP_get_int_from_string(&line_ptr);
second = logP_get_int_from_string(&line_ptr);
time_struct.tm_year = year - 1900;
time_struct.tm_mon = month - 1;
time_struct.tm_mday = day;
time_struct.tm_hour = hour;
time_struct.tm_min = minute;
time_struct.tm_sec = second;
time_struct.tm_isdst = -1; /* let sys determine DST or no */
if (line_ptr == NULL)
{
continue; /* an error - skip this line */
}
next_entry->mod_time = mkgmtime(&time_struct);
if (next_entry->mod_time == (time_t) - 1)
{
continue;
}
/*
* merged
*/
line_tmp_ptr = strstr(line_ptr, "merged:");
if (line_tmp_ptr == NULL)
{
continue; /* an error - skip this entry */
}
else
{
line_tmp_ptr += strlen("merged:");
next_entry->merged = (tolower(*line_tmp_ptr) == 'y');
if ((*line_tmp_ptr) != 0)
{
++line_tmp_ptr;
}
line_ptr = line_tmp_ptr;
}
++(log->num_entries);
}
epilogue:
util_fclose(file);
/* util_dprintf(0, "After read "); log_dump(log); */
return return_value;
}
static int
logP_write(GenLog log)
{
int return_value = 0;
STRING log_file_name = istr_string(log->log_file);
File file = NULL;
GenLogEntry entry = NULL;
int i = 0;
struct tm *time_struct = NULL;
STRING file_name = NULL;
if (log_file_name == NULL)
{
return_code(0);
}
file = util_fopen_locked(log_file_name, "wt");
if (file == NULL)
{
return_code(ERR);
}
for (i = 0; i < log->num_entries; ++i)
{
entry = &(log->entries[i]);
file_name = istr_string(entry->file_name);
/* this is mostly in case we mucked things up. */
if ((file_name == NULL) || (!util_file_exists(file_name)))
{
continue;
}
time_struct = gmtime(&(entry->mod_time));
fprintf(file, "%s %04d.%02d.%02d.%02d.%02d.%02d",
file_name,
time_struct->tm_year + 1900,
time_struct->tm_mon + 1,
time_struct->tm_mday,
time_struct->tm_hour,
time_struct->tm_min,
time_struct->tm_sec);
fprintf(file, " merged:%s", (entry->merged ? "y" : "n"));
fprintf(file, "\n");
}
epilogue:
util_fclose(file);
return return_value;
}
static int
logP_get_int_from_string(STRING * ptr_ptr)
{
#define ptr (*ptr_ptr)
int int_value = 0;
char *tok_start = NULL;
if (ptr == NULL)
{
return 0;
}
tok_start = ptr;
while (((*tok_start) != 0) && (!isdigit(*tok_start)))
{
++tok_start;
}
if ((*tok_start) != 0)
{
int_value = atoi(tok_start);
for (ptr = tok_start; ((*ptr) != 0) && isdigit(*ptr); ++ptr)
{
}
}
return int_value;
#undef ptr
}