Skip to content

Commit 637f7f7

Browse files
committed
Updated the append_line functions for safety
1 parent 43ebe69 commit 637f7f7

1 file changed

Lines changed: 120 additions & 44 deletions

File tree

utils.c

Lines changed: 120 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
*/
169184
int 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

222298
static int copy_file(const char* srcPath, const char* destPath)

0 commit comments

Comments
 (0)