@@ -166,57 +166,133 @@ char* hex_encode(unsigned char* inBuf, int len)
166166 return thumbBuf ;
167167}
168168
169+ /**
170+ * @brief Appends a line of text to a dynamically allocated string.
171+ *
172+ * This function safely reallocates the buffer pointed to by msg
173+ * to accommodate the new line, followed by a newline character.
174+ * It correctly handles the case where *msg is initially NULL.
175+ *
176+ * @param msg A pointer to a char*. On success, *msg will be updated
177+ * to point to the new, larger buffer. The caller is
178+ *
179+ * responsible for freeing this memory.
180+ * @param line The null-terminated string to append.
181+ * @return 0 on success, or an error code (EINVAL, ENOMEM) on failure.
182+ * If realloc fails, *msg remains unchanged.
183+ */
169184int append_line (char * * msg , const char * line )
170185{
171- int ret = 0 ;
172-
173- if (msg && line ) {
174- int len = strlen (* msg ) + strlen (line ) + 2 ;
175- char * tmp = realloc (* msg , len );
176- if (tmp ) {
177- strcat (tmp , line );
178- strcat (tmp , "\n" );
179- * msg = tmp ;
180- } else {
181- ret = ENOMEM ;
182- }
183- } else {
184- ret = EINVAL ;
186+ // Validate input parameters
187+ if (!msg || !line ) {
188+ return EINVAL ;
185189 }
186190
187- return ret ;
188- }
191+ // Gracefully handle the case where *msg is NULL to get the current length
192+ const size_t current_len = (* msg ) ? strlen (* msg ) : 0 ;
193+ const size_t line_len = strlen (line );
189194
190- int append_linef (char * * msg , const char * fmt , ...)
191- {
192- int ret = 0 ;
193-
194- if (msg && fmt ) {
195- va_list args ;
196- va_start (args , fmt );
197- int len = vsnprintf (NULL , 0 , fmt , args );
198- va_end (args );
199-
200- char line [len + 1 ];
201- char * tmp = realloc (* msg , strlen (* msg ) + len + 2 );
202-
203- if (tmp ) {
204- va_list args2 ;
205- va_start (args2 , fmt );
206- vsnprintf (line , len + 1 , fmt , args2 );
207- va_end (args2 );
208-
209- strcat (tmp , line );
210- strcat (tmp , "\n" );
211- * msg = tmp ;
212- } else {
213- ret = ENOMEM ;
214- }
215- } else {
216- ret = EINVAL ;
195+ // Calculate the new total size required:
196+ // current string + new line + newline char '\n' + null terminator '\0'
197+ const size_t new_size = current_len + line_len + 2 ;
198+
199+ // Reallocate memory. realloc behaves like malloc if *msg is NULL.
200+ char * new_buffer = realloc (* msg , new_size );
201+ if (!new_buffer ) {
202+ // On failure, realloc leaves the original block untouched.
203+ return ENOMEM ;
217204 }
218205
219- return ret ;
206+ // Copy the new line to the end of the old content
207+ strcpy (new_buffer + current_len , line );
208+ // Append the newline character
209+ new_buffer [current_len + line_len ] = '\n' ;
210+ // Add the new null terminator
211+ new_buffer [current_len + line_len + 1 ] = '\0' ;
212+
213+ // Update the caller's pointer to the new buffer
214+ * msg = new_buffer ;
215+
216+ return 0 ;
217+ } /* append_line */
218+
219+ /**
220+ * @brief Appends a formatted string, followed by a newline, to a dynamic buffer.
221+ *
222+ * This function operates like `printf` to create a formatted string and appends it,
223+ * along with a newline character, to the buffer pointed to by `msg`. The buffer is
224+ * automatically reallocated to the necessary size. It correctly handles the
225+ * case where `*msg` is initially `NULL`.
226+ *
227+ * @param[in,out] msg A pointer to a char pointer (`char**`). On a successful return,
228+ * `*msg` will point to the newly allocated buffer containing
229+ * the appended content. The caller is responsible for freeing
230+ * this memory using `free()`.
231+ * @param[in] fmt The `printf`-style format string.
232+ * @param[in] ... Variable arguments corresponding to the format string.
233+ *
234+ * @return 0 on success.
235+ * @return `EINVAL` if `msg` or `fmt` is `NULL`.
236+ * @return `EIO` if a formatting or encoding error occurs.
237+ * @return `ENOMEM` if a memory allocation fails.
238+ *
239+ * @note The memory pointed to by `*msg` is managed by this function via `realloc`.
240+ * The caller must not free the old pointer after a successful call,
241+ * as `realloc` may have already done so. The caller is always responsible
242+ * for freeing the final buffer.
243+ */
244+ int append_linef (char * * msg , const char * fmt , ...) {
245+ if (!msg || !fmt ) {
246+ return EINVAL ;
247+ }
248+
249+ // Determine the length of the string to be appended
250+ va_list args ;
251+ va_start (args , fmt );
252+ int append_len = vsnprintf (NULL , 0 , fmt , args );
253+ va_end (args );
254+
255+ if (append_len < 0 ) {
256+ return EIO ; // Encoding error
257+ }
258+
259+ // Allocate temporary memory for the new line
260+ char * line_to_append = malloc (append_len + 1 );
261+ if (!line_to_append ) {
262+ return ENOMEM ;
263+ }
264+
265+ // Create the new line string
266+ va_start (args , fmt );
267+ vsnprintf (line_to_append , append_len + 1 , fmt , args );
268+ va_end (args );
269+
270+ // Determine current message length (0 if msg is NULL)
271+ size_t current_len = (* msg ) ? strlen (* msg ) : 0 ;
272+
273+ // New size = current length + appended line length + newline + null terminator
274+ size_t new_size = current_len + append_len + 2 ;
275+
276+ char * new_msg = realloc (* msg , new_size );
277+ if (!new_msg ) {
278+ free (line_to_append ); // Clean up the temporary line
279+ return ENOMEM ;
280+ }
281+
282+ // If the buffer was new (current_len was 0), ensure it starts as an empty string
283+ if (current_len == 0 ) {
284+ new_msg [0 ] = '\0' ;
285+ }
286+
287+ // Concatenate the new parts
288+ strcat (new_msg , line_to_append );
289+ strcat (new_msg , "\n" );
290+
291+ // Clean up and update the caller's pointer
292+ free (line_to_append );
293+ * msg = new_msg ;
294+
295+ return 0 ; // Success
220296}
221297
222298static int copy_file (const char * srcPath , const char * destPath )
0 commit comments