/* * 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 #include #include #include #include #include #include #include #include #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 }