From e0557d10924e9d44b71a0f0b9f85823ea954a6ce Mon Sep 17 00:00:00 2001 From: Nicholas Persley Date: Mon, 5 Aug 2024 23:54:03 -0400 Subject: [PATCH] Implement use of `memccpy()` - The POSIX/C23 function `memccpy()` provides benefits that combat downfalls of other C-provided copying functions, e.g., `strcpy()` and `strcat()` * One of these downfalls is the quadratic runtime when trying to concatenate two strings using `strcat()` - New macro within `SW_Defines.h` * As previously mentioned, the `memccpy()` function is a POSIX standard or contained within C23 * The program now checks to see these conditions are met, and if they are we set the macro `sw_memccpy()` to `memccpy()` * Otherwise, if neither of these conditions are provided, we use the new custom SOILWAT2 function - `sw_memccpy_custom()` - This function provides the same functionality (see article link below) - This function also makes use of the compiler macro '__restrict' which is supported by Clang and GCC * It is not a C++ standard to have a 'restrict' keyword - Replaced all instances of `strcpy()` with `sw_memccpy()` (other than the instance in `Str_Dup()` - Replaced all instances of `strcat()` with `sw_memccpy()` - Replaced simple instances of `snprintf()` with `sw_memccpy()` * A simple instance refers to the use of `snprintf()` to write a single string that is not formatted (e.g., snprintf(..., ..., "%s", ...)) or similar - `MkDir()` gains new possible warning message * This occurs when the buffer fills and the directory is created, but by another name than what was expectedc ** For more information see https://developers.redhat.com/blog/2019/08/12/efficient-string-copying-and-concatenation-in-c#choosing_a_solution --- include/SW_Defines.h | 8 ++ include/myMemory.h | 3 + src/SW_Domain.c | 11 +- src/SW_Files.c | 6 +- src/SW_Main_lib.c | 2 +- src/SW_Output.c | 201 ++++++++++++++++++++++++----- src/SW_Output_get_functions.c | 236 ++++++++++++++++++++++++++-------- src/SW_Output_outtext.c | 37 +++++- src/SW_Site.c | 2 +- src/SW_VegEstab.c | 26 ++-- src/SW_Weather.c | 2 +- src/SW_netCDF.c | 20 +-- src/filefuncs.c | 50 +++++-- src/mymemory.c | 46 ++++++- 14 files changed, 516 insertions(+), 134 deletions(-) diff --git a/include/SW_Defines.h b/include/SW_Defines.h index 5359b238d..698ae9dae 100644 --- a/include/SW_Defines.h +++ b/include/SW_Defines.h @@ -315,6 +315,14 @@ typedef struct timespec WallTimeSpec; typedef time_t WallTimeSpec; #endif +/* Memory copying via `sw_memccpy()` and SOILWAT2's custom function */ +#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) || \ + (defined(__STDC__) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 202311L) +#define sw_memccpy memccpy +#else +#define sw_memccpy sw_memccpy_custom +#endif /* =================================================== */ /* RNG structs */ diff --git a/include/myMemory.h b/include/myMemory.h index cae71c573..6c1d794b5 100644 --- a/include/myMemory.h +++ b/include/myMemory.h @@ -31,6 +31,9 @@ void Mem_Set(void *block, byte c, size_t n); void Mem_Copy(void *dest, const void *src, size_t n); +void *sw_memccpy_custom( + void *__restrict dest, void *__restrict src, int c, size_t n +); #ifdef __cplusplus } diff --git a/src/SW_Domain.c b/src/SW_Domain.c index 905374b39..b1c9230ce 100644 --- a/src/SW_Domain.c +++ b/src/SW_Domain.c @@ -5,6 +5,7 @@ #include "include/SW_Domain.h" // for SW_DOM_CheckProgress, SW_DOM_Cre... #include "include/filefuncs.h" // for LogError, CloseFile, key_to_id #include "include/generic.h" // for swTRUE, LOGERROR, swFALSE, Bool +#include "include/myMemory.h" // for sw_memccpy_custom #include "include/SW_datastructs.h" // for SW_DOMAIN, LOG_INFO #include "include/SW_Defines.h" // for LyrIndex, LARGE_VALUE, TimeInt #include "include/SW_Files.h" // for SW_F_deconstruct, SW_F_deepCopy @@ -12,7 +13,7 @@ #include "include/Times.h" // for yearto4digit, Time_get_lastdoy_y #include // for sscanf, FILE #include // for strtod, strtol -#include // for strcmp, memcpy, strcpy, memset +#include // for strcmp, memcpy, memset #if defined(SWNETCDF) #include "include/SW_netCDF.h" @@ -268,8 +269,8 @@ void SW_DOM_read(SW_DOMAIN *SW_Domain, LOG_INFO *LogInfo) { ); goto closeFile; } - (void) snprintf( - SW_Domain->DomainType, sizeof SW_Domain->DomainType, "%s", value + (void) sw_memccpy( + SW_Domain->DomainType, value, '\0', sizeof SW_Domain->DomainType ); break; case 1: // Number of X slots @@ -333,8 +334,8 @@ void SW_DOM_read(SW_DOMAIN *SW_Domain, LOG_INFO *LogInfo) { goto closeFile; } - (void) snprintf( - SW_Domain->crs_bbox, sizeof SW_Domain->crs_bbox, "%s", value + (void) sw_memccpy( + SW_Domain->crs_bbox, value, '\0', sizeof SW_Domain->crs_bbox ); break; case 9: // Minimum x coordinate diff --git a/src/SW_Files.c b/src/SW_Files.c index 8cda4ecee..10f1b2895 100644 --- a/src/SW_Files.c +++ b/src/SW_Files.c @@ -39,7 +39,7 @@ #include "include/SW_Defines.h" // for MAX_FILENAMESIZE #include // for FILENAME_MAX, NULL, FILE, stderr #include // for free -#include // for strcpy, strcmp, strlen, memcpy +#include // for memccpy, strcmp, strlen, memcpy /* =================================================== */ /* Global Function Definitions */ @@ -66,7 +66,7 @@ void SW_CSV_F_INIT(const char *s, LOG_INFO *LogInfo) { DirName(s, dirString); if (DirExists(dirString)) { - (void) snprintf(inbuf, sizeof inbuf, "%s", s); + (void) sw_memccpy(inbuf, (char *) s, '\0', sizeof inbuf); if (!RemoveFiles(inbuf, LogInfo)) { LogError( LogInfo, LOGWARN, "Can't remove old csv output file: %s\n", s @@ -348,7 +348,7 @@ void SW_F_construct( c = dirString; if (c) { - strcpy(SW_ProjDir, c); + (void) sw_memccpy(SW_ProjDir, c, '\0', sizeof dirString); c = localfirstfile; p = c + strlen(SW_ProjDir); while (*p) { diff --git a/src/SW_Main_lib.c b/src/SW_Main_lib.c index 29ee054e0..eff30f983 100644 --- a/src/SW_Main_lib.c +++ b/src/SW_Main_lib.c @@ -24,7 +24,7 @@ #include "include/SW_Defines.h" // for MAX_MSGS, MAX_LOG_SIZE, BUILD_DATE #include // for fprintf, stderr, fflush, stdout #include // for exit, free, EXIT_FA... -#include // for strcpy, strncmp +#include // for strncmp #ifdef RSOILWAT #include // for error(), and warning() from diff --git a/src/SW_Output.c b/src/SW_Output.c index 70b0c5d8b..b390f35e2 100644 --- a/src/SW_Output.c +++ b/src/SW_Output.c @@ -44,7 +44,7 @@ See the \ref out_algo "output algorithm documentation" for details. #include "include/SW_VegProd.h" // for echo_VegProd #include "include/Times.h" // for Time_days_in_month, WKDAYS #include // for snprintf, fprintf, printf -#include // for strcat, strcmp, strcpy, memset +#include // for strcmp, memccpy, memset // Array-based output declarations: #if defined(SW_OUTARRAY) || defined(SWNETCDF) @@ -2161,7 +2161,9 @@ void SW_OUT_set_colnames( for (i = 0; i < ncol_OUT[eSW_Temp]; i++) { if (i < 3) { // Normal air temperature columns - (void) snprintf(ctemp, sizeof ctemp, "%s", cnames_eSW_Temp[i]); + (void) sw_memccpy( + ctemp, (char *) cnames_eSW_Temp[i], '\0', sizeof ctemp + ); } else { // Surface temperature columns (void) snprintf( @@ -2540,7 +2542,7 @@ void SW_OUT_set_colnames( } #endif i = 0; - (void) snprintf(ctemp, sizeof ctemp, "%s", "fCover_BareGround"); + (void) sw_memccpy(ctemp, "fCover_BareGround", '\0', sizeof ctemp); colnames_OUT[eSW_Biomass][i] = Str_Dup(ctemp, LogInfo); if (LogInfo->stopRun) { return; // Exit function prematurely due to error @@ -3216,9 +3218,39 @@ void SW_OUT_write_today( int i; int outPeriod; +#ifdef SW_OUTTEXT + char *resPtr = NULL; + + char *soilWritePtr[SW_OUTNPERIODS] = { + sw->FileStatus.buf_soil[0], + sw->FileStatus.buf_soil[1], + sw->FileStatus.buf_soil[2], + sw->FileStatus.buf_soil[3] + }; + char *regWritePtr[SW_OUTNPERIODS] = { + sw->FileStatus.buf_reg[0], + sw->FileStatus.buf_reg[1], + sw->FileStatus.buf_reg[2], + sw->FileStatus.buf_reg[3] + }; +#endif + #ifdef STEPWAT Bool use_help_txt; Bool use_help_SXW; + + char *soilAggWritePtr[SW_OUTNPERIODS] = { + sw->FileStatus.buf_soil[0], + sw->FileStatus.buf_soil[1], + sw->FileStatus.buf_soil[2], + sw->FileStatus.buf_soil[3] + }; + char *regAggWritePtr[SW_OUTNPERIODS] = { + sw->FileStatus.buf_reg[0], + sw->FileStatus.buf_reg[1], + sw->FileStatus.buf_reg[2], + sw->FileStatus.buf_reg[3] + }; #endif @@ -3280,6 +3312,37 @@ void SW_OUT_write_today( // formatting functions `get_XXX`, and concatenate for one row of // `csv`-output ForEachOutKey(k) { +#ifdef SW_OUTTEXT + size_t writeSizeReg[SW_OUTNPERIODS] = { + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN) + }; + + size_t writeSizeSoil[SW_OUTNPERIODS] = { + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN) + }; +#endif + +#ifdef STEPWAT + size_t writeSizeSoilAgg[SW_OUTNPERIODS] = { + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN) + }; + size_t writeSizeRegAgg[SW_OUTNPERIODS] = { + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN), + (size_t) (MAX_LAYERS * OUTSTRLEN) + }; +#endif + #ifdef SWDEBUG if (debug) { sw_printf("key=%d=%s: ", k, key2str[k]); @@ -3291,6 +3354,7 @@ void SW_OUT_write_today( } for (i = 0; i < OutDom->used_OUTNPERIODS; i++) { + outPeriod = OutDom->timeSteps[k][i]; use_help = (Bool) (outPeriod != eSW_NoTime && writeit[outPeriod]); @@ -3376,22 +3440,66 @@ void SW_OUT_write_today( #if defined(SW_OUTTEXT) /* concatenate formatted output for one row of `csv`- files */ if (OutDom->print_SW_Output) { - strcpy(tempstr, sw->OutRun.sw_outstr); + (void) sw_memccpy( + tempstr, + sw->OutRun.sw_outstr, + '\0', + (size_t) (MAX_LAYERS * OUTSTRLEN) + ); + if (OutDom->has_sl[k]) { - strcat(sw->FileStatus.buf_soil[outPeriod], tempstr); + resPtr = (char *) sw_memccpy( + soilWritePtr[outPeriod], + tempstr, + '\0', + writeSizeSoil[outPeriod] + ); + soilWritePtr[outPeriod] = resPtr - 1; + writeSizeSoil[outPeriod] -= + (resPtr - soilWritePtr[outPeriod] - 1); } else { - strcat(sw->FileStatus.buf_reg[outPeriod], tempstr); + resPtr = (char *) sw_memccpy( + regWritePtr[outPeriod], + tempstr, + '\0', + writeSizeReg[outPeriod] + ); + regWritePtr[outPeriod] = resPtr - 1; + writeSizeReg[outPeriod] -= + (resPtr - regWritePtr[outPeriod] - 1); } } #ifdef STEPWAT if (OutDom->print_IterationSummary) { - strcpy(tempstr, sw->OutRun.sw_outstr_agg); + (void) sw_memccpy( + tempstr, + sw->OutRun.sw_outstr_agg, + '\0', + (size_t) (MAX_LAYERS * OUTSTRLEN) + ); + if (OutDom->has_sl[k]) { - strcat(sw->FileStatus.buf_soil_agg[outPeriod], tempstr); + resPtr = sw_memccpy( + soilAggWritePtr[outPeriod], + tempstr, + '\0', + writeSizeSoilAgg[outPeriod] + ); + soilAggWritePtr[outPeriod] = resPtr - 1; + writeSizeSoilAgg[outPeriod] -= + (resPtr - soilAggWritePtr[outPeriod] - 1); } else { - strcat(sw->FileStatus.buf_reg_agg[outPeriod], tempstr); + resPtr = sw_memccpy( + regAggWritePtr[outPeriod], + tempstr, + '\0', + writeSizeRegAgg[outPeriod] + ); + regAggWritePtr[outPeriod] = resPtr - 1; + writeSizeRegAgg[outPeriod] -= + (resPtr - regAggWritePtr[outPeriod] - 1); } } #endif @@ -3608,39 +3716,72 @@ void SW_OUT_close_files( void echo_outputs(SW_OUT_DOM *OutDom) { int k; + int index; + int writeValIndex = 0; char str[OUTSTRLEN]; char errstr[MAX_ERROR]; + size_t writeSize = MAX_ERROR; + char *writePtr = errstr; + char *resPtr = NULL; + char *cpyPtr = NULL; + const int numWriteStrs = 6; + const size_t errHeaderSize = 75; + + const char *errStrHeader = "---------------------------\nKey "; + const char *errStrFooter = + "\n---------- End of Output Configuration ---------- \n"; - (void) snprintf( - errstr, - sizeof errstr, - "%s", + const char *errStrConf = "\n===============================================\n" - " Output Configuration:\n" - ); + " Output Configuration:\n"; + + char *writeStrs[] = { + (char *) errStrHeader, + (char *) key2str[0], /* Overwrite in loop below */ + (char *) "\n\tSummary Type: ", + (char *) styp2str[0], /* Overwrite in loop below */ + (char *) "\n\tStart period: %d", + (char *) "\n\tEnd period : %d\n" + }; + + TimeInt writeVals[] = { + OutDom->first_orig[0], /* Overwrite in loop below */ + OutDom->last_orig[0] /* Overwrite in loop below */ + }; + + (void) sw_memccpy(errstr, (char *) errStrConf, '\0', errHeaderSize); ForEachOutKey(k) { - if (!OutDom->use[k]) { + if (OutDom->use[k]) { + writeStrs[1] = (char *) key2str[k]; + writeStrs[3] = (char *) styp2str[OutDom->sumtype[k]]; + + writeVals[0] = OutDom->first_orig[k]; + writeVals[0] = OutDom->last_orig[k]; + } else { continue; } - strcat(errstr, "---------------------------\nKey "); - strcat(errstr, key2str[k]); - strcat(errstr, "\n\tSummary Type: "); - strcat(errstr, styp2str[OutDom->sumtype[k]]); - (void) snprintf( - str, OUTSTRLEN, "\n\tStart period: %d", OutDom->first_orig[k] - ); - strcat(errstr, str); - (void) snprintf( - str, OUTSTRLEN, "\n\tEnd period : %d", OutDom->last_orig[k] - ); - strcat(errstr, str); - strcat(errstr, "\n"); + + for (index = 0; index < numWriteStrs; index++) { + cpyPtr = writeStrs[index]; + + if (index > 4) { + (void + ) snprintf(str, OUTSTRLEN, cpyPtr, writeVals[writeValIndex]); + + writeValIndex++; + cpyPtr = str; + } + + resPtr = (char *) sw_memccpy(writePtr, cpyPtr, '\0', writeSize); + writeSize -= (resPtr - writePtr - 1); + writePtr = resPtr - 1; + } } - strcat(errstr, "\n---------- End of Output Configuration ---------- \n"); + sw_memccpy(writePtr, (char *) errStrFooter, '\0', writeSize); printf("%s\n", errstr); } diff --git a/src/SW_Output_get_functions.c b/src/SW_Output_get_functions.c index 233ee545b..d7a4a7cc5 100644 --- a/src/SW_Output_get_functions.c +++ b/src/SW_Output_get_functions.c @@ -19,6 +19,7 @@ See the \ref out_algo "output algorithm documentation" for details. /* --------------------------------------------------- */ #include "include/generic.h" // for IntU +#include "include/myMemory.h" // for sw_memccpy_custom #include "include/SW_datastructs.h" // for SW_RUN, SW_OUTTEXT #include "include/SW_Defines.h" // for OUTSEP, OUT_DIGITS, OUTSTRLEN #include "include/SW_Output.h" // for get_aet_text, get_biomass_text @@ -40,7 +41,7 @@ See the \ref out_algo "output algorithm documentation" for details. #if defined(SW_OUTTEXT) #include // for snprintf, NULL -#include // for strcat +#include // for memccpy #endif @@ -61,6 +62,9 @@ static void format_IterationSummary( size_t n; double sd; char str[OUTSTRLEN]; + size_t writeSize = OUTSTRLEN; + char *writePtr = sw->OutRun.sw_outstr_agg; + char *resPtr = NULL; SW_OUT_RUN *OutRun = &sw->OutRun; for (i = 0; i < N; i++) { @@ -78,7 +82,9 @@ static void format_IterationSummary( OUT_DIGITS, sd ); - strcat(OutRun->sw_outstr_agg, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr_agg - 1); } } @@ -96,6 +102,10 @@ static void format_IterationSummary2( size_t n; double sd; char str[OUTSTRLEN]; + size_t strLen = strlen(sw->OutRun.sw_outstr_agg); + size_t writeSize = OUTSTRLEN - strLen; + char *writePtr = sw->OutRun.sw_outstr_agg + strLen; + char *resPtr = NULL; SW_OUT_RUN *OutRun = &sw->OutRun; for (k = 0; k < N1; k++) { @@ -116,7 +126,9 @@ static void format_IterationSummary2( OUT_DIGITS, sd ); - strcat(OutRun->sw_outstr_agg, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr_agg - 1); } } } @@ -184,6 +196,9 @@ void get_co2effects_text(OutPeriod pd, SW_RUN *sw) { char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; TimeInt simyear = sw->Model.simyear; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; (void) pd; // hack to silence "-Wunused-parameter" @@ -196,7 +211,9 @@ void get_co2effects_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, sw->VegProd.veg[k].co2_multipliers[BIO_INDEX][simyear] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } ForEachVegType(k) { (void) snprintf( @@ -207,7 +224,9 @@ void get_co2effects_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, sw->VegProd.veg[k].co2_multipliers[WUE_INDEX][simyear] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -323,6 +342,9 @@ void get_biomass_text(OutPeriod pd, SW_RUN *sw) { int k; SW_VEGPROD_OUTPUTS *vo = sw->VegProd.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -336,7 +358,10 @@ void get_biomass_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, sw->VegProd.bare_cov.fCover ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); + ForEachVegType(k) { (void) snprintf( str, @@ -346,13 +371,18 @@ void get_biomass_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, sw->VegProd.veg[k].cov.fCover ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } // biomass (g/m2 as component of total) for NVEGTYPES plus totals and litter (void ) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->biomass_total); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); + ForEachVegType(k) { (void) snprintf( str, @@ -362,16 +392,23 @@ void get_biomass_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->veg[k].biomass_inveg ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } (void ) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->litter_total); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); // biolive (g/m2 as component of total) for NVEGTYPES plus totals (void ) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->biolive_total); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); + ForEachVegType(k) { (void) snprintf( str, @@ -381,12 +418,14 @@ void get_biomass_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->veg[k].biolive_inveg ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } // leaf area index [m2/m2] (void) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->LAI); - strcat(OutRun->sw_outstr, str); + sw_memccpy(writePtr, str, '\0', writeSize); } #endif @@ -670,6 +709,9 @@ establish this year. This check is for OUTTEXT. void get_estab_text(OutPeriod pd, SW_RUN *sw) { IntU i; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -680,7 +722,9 @@ void get_estab_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%d", OUTSEP, sw->VegEstab.parms[i]->estab_doy ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -1211,6 +1255,9 @@ void get_vwcBulk_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -1225,7 +1272,9 @@ void get_vwcBulk_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->vwcBulk[i] / sw->Site.width[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -1347,6 +1396,9 @@ void get_vwcMatric_text(OutPeriod pd, SW_RUN *sw) { double convert; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -1364,7 +1416,9 @@ void get_vwcMatric_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->vwcMatric[i] * convert ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -1490,6 +1544,9 @@ void get_swa_text(OutPeriod pd, SW_RUN *sw) { int k; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -1504,7 +1561,9 @@ void get_swa_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->SWA_VegType[k][i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } } @@ -1633,6 +1692,9 @@ void get_swcBulk_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -1641,7 +1703,9 @@ void get_swcBulk_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->swcBulk[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -1785,6 +1849,9 @@ void get_swpMatric_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; /* Local LOG_INFO only because `SW_SWRC_SWCtoSWP()` requires it */ LOG_INFO local_log; @@ -1799,7 +1866,9 @@ void get_swpMatric_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, val); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -1920,6 +1989,9 @@ void get_swaBulk_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -1928,7 +2000,9 @@ void get_swaBulk_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->swaBulk[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -2041,6 +2115,9 @@ void get_swaMatric_text(OutPeriod pd, SW_RUN *sw) { double convert; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -2057,7 +2134,9 @@ void get_swaMatric_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->swaMatric[i] * convert ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -2438,10 +2517,13 @@ void get_transp_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; LyrIndex n_layers = sw->Site.n_layers; int k; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + char *writeStr = OutRun->sw_outstr; + char *resPtr = NULL; - char str[OUTSTRLEN]; + char str[OUTSTRLEN] = {'\0'}; OutRun->sw_outstr[0] = '\0'; /* total transpiration */ @@ -2449,7 +2531,9 @@ void get_transp_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->transp_total[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writeStr, str, '\0', writeSize); + writeSize -= (resPtr - OutRun->sw_outstr - 1); + writeStr = resPtr - 1; } /* transpiration for each vegetation type */ @@ -2458,7 +2542,9 @@ void get_transp_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->transp[k][i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writeStr, str, '\0', writeSize); + writeSize -= (resPtr - OutRun->sw_outstr - 1); + writeStr = resPtr - 1; } } } @@ -2670,6 +2756,9 @@ void get_evapSoil_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -2678,7 +2767,9 @@ void get_evapSoil_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->evap_baresoil[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -2793,24 +2884,26 @@ void get_evapSurface_text(OutPeriod pd, SW_RUN *sw) { int k; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; - (void) snprintf( - OutRun->sw_outstr, - sizeof OutRun->sw_outstr, - "%c%.*f", - OUTSEP, - OUT_DIGITS, - vo->total_evap - ); + (void + ) snprintf(str, sizeof str, "%c%.*f", OUTSEP, OUT_DIGITS, vo->total_evap); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); ForEachVegType(k) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->evap_veg[k] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } (void) snprintf( @@ -2824,7 +2917,7 @@ void get_evapSurface_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->surfaceWater_evap ); - strcat(OutRun->sw_outstr, str); + sw_memccpy(writePtr, str, '\0', writeSize); } #endif @@ -2990,29 +3083,31 @@ void get_interception_text(OutPeriod pd, SW_RUN *sw) { int k; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; - (void) snprintf( - OutRun->sw_outstr, - sizeof OutRun->sw_outstr, - "%c%.*f", - OUTSEP, - OUT_DIGITS, - vo->total_int - ); + (void + ) snprintf(str, sizeof str, "%c%.*f", OUTSEP, OUT_DIGITS, vo->total_int); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); ForEachVegType(k) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->int_veg[k] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } (void ) snprintf(str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->litter_int); - strcat(OutRun->sw_outstr, str); + sw_memccpy(writePtr, str, '\0', writeSize); } #endif @@ -3256,6 +3351,9 @@ void get_lyrdrain_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -3264,7 +3362,9 @@ void get_lyrdrain_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->lyrdrain[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -3380,6 +3480,9 @@ void get_hydred_text(OutPeriod pd, SW_RUN *sw) { int k; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -3389,7 +3492,9 @@ void get_hydred_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->hydred_total[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } /* hydraulic redistribution for each vegetation type */ @@ -3398,7 +3503,9 @@ void get_hydred_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->hydred[k][i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } } @@ -3966,6 +4073,9 @@ void get_wetdays_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; LyrIndex n_layers = sw->Site.n_layers; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -3975,7 +4085,9 @@ void get_wetdays_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%i", OUTSEP, (sw->SoilWat.is_wet[i]) ? 1 : 0 ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } else { @@ -3984,7 +4096,9 @@ void get_wetdays_text(OutPeriod pd, SW_RUN *sw) { ForEachSoilLayer(i, n_layers) { (void ) snprintf(str, OUTSTRLEN, "%c%i", OUTSEP, (int) vo->wetdays[i]); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } } @@ -4332,6 +4446,9 @@ void get_soiltemp_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -4345,7 +4462,9 @@ void get_soiltemp_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->maxLyrTemperature[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); (void) snprintf( str, @@ -4355,12 +4474,16 @@ void get_soiltemp_text(OutPeriod pd, SW_RUN *sw) { OUT_DIGITS, vo->minLyrTemperature[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->avgLyrTemp[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif @@ -4551,6 +4674,9 @@ void get_frozen_text(OutPeriod pd, SW_RUN *sw) { LyrIndex i; SW_SOILWAT_OUTPUTS *vo = sw->SoilWat.p_oagg[pd]; SW_OUT_RUN *OutRun = &sw->OutRun; + size_t writeSize = (size_t) (MAX_LAYERS * OUTSTRLEN); + char *writePtr = OutRun->sw_outstr; + char *resPtr = NULL; char str[OUTSTRLEN]; OutRun->sw_outstr[0] = '\0'; @@ -4559,7 +4685,9 @@ void get_frozen_text(OutPeriod pd, SW_RUN *sw) { (void) snprintf( str, OUTSTRLEN, "%c%.*f", OUTSEP, OUT_DIGITS, vo->lyrFrozen[i] ); - strcat(OutRun->sw_outstr, str); + resPtr = (char *) sw_memccpy(writePtr, str, '\0', writeSize); + writePtr = resPtr - 1; + writeSize -= (resPtr - OutRun->sw_outstr - 1); } } #endif diff --git a/src/SW_Output_outtext.c b/src/SW_Output_outtext.c index 18117eb9e..a65aa343b 100644 --- a/src/SW_Output_outtext.c +++ b/src/SW_Output_outtext.c @@ -27,7 +27,7 @@ See the \ref out_algo "output algorithm documentation" for details. #include "include/SW_Output.h" // for pd2longstr, ForEachOutKey #include // for snprintf, fflush, fprintf #include // for free -#include // for strcat, strcpy +#include // for memccpy #if (defined(SOILWAT) && !defined(SWNETCDF)) || defined(STEPWAT) #include "include/SW_Files.h" // for eOutputDaily, eOutputDaily_soil @@ -88,6 +88,14 @@ static void create_csv_headers( char *str_help1; char *str_help2; + size_t writeSizeHelp = size_help; + size_t writeSizeReg = (size_t) (2 * OUTSTRLEN); + size_t writeSizeSoil = (size_t) (n_layers) *OUTSTRLEN; + char *writePtrHelp = NULL; + char *resPtr = NULL; + char *writePtrSoil = NULL; + char *writePtrReg = NULL; + str_help1 = (char *) Mem_Malloc( sizeof(char) * size_help, "create_csv_headers()", LogInfo ); @@ -107,7 +115,12 @@ static void create_csv_headers( str_reg[0] = (char) '\0'; str_soil[0] = (char) '\0'; + writePtrReg = str_reg; + writePtrSoil = str_soil; + ForEachOutKey(k) { + writePtrHelp = str_help2; + isTrue = (Bool) (OutDom->use[k] && has_OutPeriod_inUse( pd, (OutKey) k, @@ -115,7 +128,7 @@ static void create_csv_headers( OutDom->timeSteps )); if (isTrue) { - (void) snprintf(key, sizeof key, "%s", key2str[k]); + (void) sw_memccpy(key, (char *) key2str[k], '\0', sizeof key); str_help2[0] = '\0'; for (i = 0; i < OutDom->ncol_OUT[k]; i++) { @@ -147,13 +160,25 @@ static void create_csv_headers( goto freeMem; // Exit function prematurely due to error } - strcat(str_help2, str_help1); + resPtr = (char *) sw_memccpy( + writePtrHelp, str_help1, '\0', writeSizeHelp + ); + writePtrHelp = resPtr - 1; + writeSizeHelp -= (resPtr - writePtrHelp - 1); } if (OutDom->has_sl[k]) { - strcat(str_soil, str_help2); + resPtr = (char *) sw_memccpy( + writePtrSoil, str_help2, '\0', writeSizeSoil + ); + writeSizeSoil -= (resPtr - str_soil - 1); + writePtrSoil = resPtr - 1; } else { - strcat(str_reg, str_help2); + resPtr = (char *) sw_memccpy( + writePtrReg, str_help2, '\0', writeSizeReg + ); + writeSizeReg -= (resPtr - str_reg - 1); + writePtrReg = resPtr - 1; } } } @@ -227,7 +252,7 @@ static void get_outstrheader(OutPeriod pd, char *str, size_t sizeof_str) { break; case eSW_Year: - (void) snprintf(str, sizeof_str, "%s", "Year"); + (void) sw_memccpy(str, "Year", '\0', sizeof_str); break; default: diff --git a/src/SW_Site.c b/src/SW_Site.c index 0f238aa77..ba8a92aec 100644 --- a/src/SW_Site.c +++ b/src/SW_Site.c @@ -122,7 +122,7 @@ #include // for fmod #include // for printf, sscanf, FILE, NULL, stdout #include // for free, strod, strtol -#include // for strcpy, memset +#include // for memset /* =================================================== */ /* Global Variables */ diff --git a/src/SW_VegEstab.c b/src/SW_VegEstab.c index 2ac7f0e75..5106ec59d 100644 --- a/src/SW_VegEstab.c +++ b/src/SW_VegEstab.c @@ -51,7 +51,7 @@ #include // for fabs #include // for NULL, snprintf, FILE, printf #include // for free -#include // for strcpy, strcat, strlen, memset +#include // for memccpy, strlen, memset /* =================================================== */ @@ -715,7 +715,7 @@ static void read_spp( goto closeFile; } - (void) snprintf(v->sppname, sizeof v->sppname, "%s", name); + (void) sw_memccpy(v->sppname, name, '\0', sizeof(v->sppname)); } lineno++; /*only increments when there's a value */ @@ -927,9 +927,16 @@ IntU new_species(SW_VEGESTAB *SW_VegEstab, LOG_INFO *LogInfo) { void echo_VegEstab(const double width[], SW_VEGESTAB_INFO **parms, IntU count) { /* --------------------------------------------------- */ IntU i; - char outstr[2048]; + char outstr[MAX_ERROR]; char errstr[MAX_ERROR]; + const char *endDispStr = + "\n----------------- End of Establishment Parameters ------------\n"; + + size_t writeSize = MAX_ERROR; + char *writePtr = outstr; + char *resPtr = NULL; + (void) snprintf( errstr, MAX_ERROR, @@ -940,7 +947,7 @@ void echo_VegEstab(const double width[], SW_VEGESTAB_INFO **parms, IntU count) { count ); - (void) snprintf(outstr, sizeof outstr, "%s", errstr); + (void) sw_memccpy(outstr, errstr, '\0', sizeof outstr); for (i = 0; i < count; i++) { (void) snprintf( @@ -996,12 +1003,13 @@ void echo_VegEstab(const double width[], SW_VEGESTAB_INFO **parms, IntU count) { parms[i]->max_drydays_postgerm ); - strcat(outstr, errstr); + resPtr = (char *) sw_memccpy( + (void *) writePtr, (void *) errstr, '\0', writeSize + ); + writePtr = resPtr - 1; + writeSize -= (resPtr - outstr - 1); } - strcat( - outstr, - "\n----------------- End of Establishment Parameters ------------\n" - ); + sw_memccpy(outstr, (char *) endDispStr, '\0', writeSize); printf("%s\n", outstr); } diff --git a/src/SW_Weather.c b/src/SW_Weather.c index 9398fccc6..bfbe72ba7 100644 --- a/src/SW_Weather.c +++ b/src/SW_Weather.c @@ -94,7 +94,7 @@ #include // for exp, fmin, fmax #include // for NULL, sscanf, FILE, fclose, fopen #include // for free -#include // for memset, NULL, strcpy +#include // for memset, NULL /* =================================================== */ /* Local Function Definitions */ diff --git a/src/SW_netCDF.c b/src/SW_netCDF.c index 0abe383de..699433478 100644 --- a/src/SW_netCDF.c +++ b/src/SW_netCDF.c @@ -2936,10 +2936,11 @@ static void create_output_dimVar( if (dimNum == vertIndex && !hasConsistentSoilLayerDepths) { // Use soil layers as dimension variable values // because soil layer depths are not consistent across domain - (void) snprintf( - outAttVals[vertIndex][0], MAX_FILENAMESIZE, "soil layer" + (void) sw_memccpy( + outAttVals[vertIndex][0], "soil layer", '\0', MAX_FILENAMESIZE ); - (void) snprintf(outAttVals[vertIndex][2], MAX_FILENAMESIZE, "1"); + (void + ) sw_memccpy(outAttVals[vertIndex][2], "1", '\0', MAX_FILENAMESIZE); } for (index = 0; index < numVarAtts[dimNum]; index++) { @@ -3254,7 +3255,7 @@ static void create_output_file( char *varName; char **varInfo; - (void) snprintf(frequency, 9, "%s", pd2longstr[pd]); + (void) sw_memccpy(frequency, (char *) pd2longstr[pd], '\0', 9); Str_ToLower(frequency, frequency); @@ -3800,7 +3801,9 @@ void SW_NC_create_output_files( baseTime = times[pd]; rangeStart = startYr; - (void) snprintf(periodSuffix, 9, "%s", pd2longstr[pd]); + (void) sw_memccpy( + periodSuffix, (char *) pd2longstr[pd], '\0', 9 + ); Str_ToLower(periodSuffix, periodSuffix); SW_NC_alloc_files( @@ -5134,11 +5137,8 @@ void SW_NC_read_out_vars( break; case LONGNAME_INDEX: - (void) snprintf( - establn, - MAX_ATTVAL_SIZE - 1, - copyStr, - parms[estVar]->sppname + (void) sw_memccpy( + establn, copyStr, '\0', MAX_ATTVAL_SIZE ); OutDom->outputVarInfo[currOutKey][estVar][index] = Str_Dup(establn, LogInfo); diff --git a/src/filefuncs.c b/src/filefuncs.c index c52a1d0c5..d3960c477 100644 --- a/src/filefuncs.c +++ b/src/filefuncs.c @@ -23,7 +23,7 @@ #include // for va_end, va_start #include // for NULL, fclose, FILE, fopen, EOF #include // for free, strtod, strtof, strtol -#include // for strlen, strrchr, strcpy, strchr +#include // for strlen, strrchr, memccpy, strchr #include // for stat, mkdir, S_ISDIR, S_ISREG #include // for chdir @@ -187,13 +187,14 @@ void LogError(LOG_INFO *LogInfo, const int mode, const char *fmt, ...) { int nextWarn = LogInfo->numWarnings; va_list args; int expectedWriteSize; + char *writePtr = msgType; va_start(args, fmt); if (LOGWARN & mode) { - (void) snprintf(msgType, sizeof msgType, "%s", "WARNING: "); + (void) sw_memccpy(writePtr, "WARNING: ", '\0', MAX_LOG_SIZE); } else if (LOGERROR & mode) { - (void) snprintf(msgType, sizeof msgType, "%s", "ERROR: "); + (void) sw_memccpy(writePtr, "ERROR: ", '\0', MAX_LOG_SIZE); } expectedWriteSize = snprintf(outfmt, MAX_LOG_SIZE, "%s%s\n", msgType, fmt); @@ -225,11 +226,8 @@ void LogError(LOG_INFO *LogInfo, const int mode, const char *fmt, ...) { if (LOGWARN & mode) { if (nextWarn < MAX_MSGS) { - (void) snprintf( - LogInfo->warningMsgs[nextWarn], - sizeof LogInfo->warningMsgs[nextWarn], - "%s", - buf + (void) sw_memccpy( + LogInfo->warningMsgs[nextWarn], buf, '\0', MAX_LOG_SIZE ); } LogInfo->numWarnings++; @@ -243,7 +241,7 @@ void LogError(LOG_INFO *LogInfo, const int mode, const char *fmt, ...) { // as SOILWAT2 >= 7.2.0 exit(EXIT_FAILURE); #else - (void) snprintf(LogInfo->errorMsg, sizeof LogInfo->errorMsg, "%s", buf); + (void) sw_memccpy(LogInfo->errorMsg, buf, '\0', MAX_LOG_SIZE); LogInfo->stopRun = swTRUE; #endif } @@ -634,9 +632,12 @@ void MkDir(const char *dname, LOG_INFO *LogInfo) { char *c; /* duplicate of dname so we don't change it */ const char *delim = "\\/"; /* path separators */ char buffer[MAX_ERROR]; + char *writePtr = buffer; + char *resPtr = NULL; size_t startIndex = 0; size_t strLen = 0; // For `sw_strtok()` + size_t writeSize = 256; if (isnull(dname)) { return; @@ -656,17 +657,36 @@ void MkDir(const char *dname, LOG_INFO *LogInfo) { buffer[0] = '\0'; for (i = 0; i < n; i++) { - strcat(buffer, a[i]); - if (!DirExists(buffer)) { + if (!isnull(resPtr) || i == 0) { + resPtr = (char *) sw_memccpy(writePtr, a[i], '\0', writeSize); + writeSize -= (resPtr - buffer - 1); + writePtr = resPtr - 1; + } + + if (!DirExists(buffer) || isnull(resPtr)) { if (0 != mkdir(buffer, 0777)) { // directory failed to create -> report error LogError( LogInfo, LOGERROR, "Failed to create directory '%s'", buffer ); goto freeMem; // Exit function prematurely due to error + } else { + /* Directory was created but not by the expected name */ + LogError( + LogInfo, + LOGWARN, + "Could not create the desired directory. The path created" + "instead is %s.", + buffer + ); } } - strcat(buffer, "/"); + + if (!isnull(resPtr)) { + resPtr = (char *) sw_memccpy(writePtr, "/", '\0', writeSize); + writePtr = resPtr - 1; + writeSize--; + } } freeMem: @@ -690,10 +710,12 @@ Bool RemoveFiles(const char *fspec, LOG_INFO *LogInfo) { char **flist; char fname[FILENAME_MAX]; + char *resPtr = NULL; int i; int nfiles; int result = swTRUE; size_t dlen; + size_t writeSize = FILENAME_MAX; if (fspec == NULL) { return swTRUE; @@ -705,7 +727,9 @@ Bool RemoveFiles(const char *fspec, LOG_INFO *LogInfo) { DirName(fspec, fname); // Transfer `fspec` into `fname` dlen = strlen(fname); for (i = 0; i < nfiles; i++) { - strcpy(fname + dlen, flist[i]); + resPtr = + (char *) sw_memccpy(fname + dlen, flist[i], '\0', writeSize); + writeSize -= (resPtr - (fname + dlen) - 1); if (0 != remove(fname)) { result = swFALSE; break; diff --git a/src/mymemory.c b/src/mymemory.c index 6e5dcb87d..b6482f175 100644 --- a/src/mymemory.c +++ b/src/mymemory.c @@ -34,7 +34,7 @@ #include "include/generic.h" // for LOGERROR, byte, isnull #include "include/SW_datastructs.h" // for LOG_INFO #include // for free, malloc, realloc -#include // for strlen, memcpy, memset, strcpy +#include // for strlen, memset, strcpy /* =================================================== */ /* Global Function Definitions */ @@ -183,5 +183,49 @@ void Mem_Copy(void *dest, const void *src, size_t n) { memcpy(dest, src, n); } +/* +@brief Custom functionality that mimics that of `sw_memccpy()` and is used +when the correct dependencies for `sw_memccpy()` are not available + +@note This implementation is based off one suggested by Martin Sebor in +the article +https://developers.redhat.com/blog/2019/08/12/efficient-string-copying-and-concatenation-in-c# + +@note This function uses the compiler macro '__restrict' instead of simply +'restrict' due to C++ standards not supporting it, so '__restrict' is +compatible in Clang and GCC + +@param[in,out] dest Character array to copy into +@param[in] src Character array to copy from +@param[in] c Target character which, upon finding, is one of the stopping +condiditions +@param[in] n The number of bytes to copy from src to dest, and is the +second stopping condition + +@return +Upon finding the target character: the pointer to the next byte in dest after +the copy Upon not finding the target character: null pointer character +*/ +void *sw_memccpy_custom( + void *__restrict dest, void *__restrict src, int c, size_t n +) { + char *s = (char *) src; + char *ret = (char *) dest; + + while (n > 0) { + *ret = *s; + + if ((unsigned char) *ret == (unsigned char) c) { + return ret + 1; + } + + ret++; + s++; + n--; + } + + return 0; +} + /* =============== end of block from gen_funcs.c ----------------- */ /* ================ see also the end of this file ------------------ */