libarmutils  1.4
 All Data Structures Files Functions Variables Typedefs Enumerations Macros Groups
file_buffer.c
Go to the documentation of this file.
1 /*******************************************************************************
2 *
3 * COPYRIGHT (C) 2017 Battelle Memorial Institute. All Rights Reserved.
4 *
5 ********************************************************************************
6 *
7 * Author:
8 * name: Brian Ermold
9 * phone: (509) 375-2277
10 * email: brian.ermold@pnl.gov
11 *
12 ********************************************************************************
13 *
14 * REPOSITORY INFORMATION:
15 * $Revision:$
16 * $Author:$
17 * $Date:$
18 *
19 ********************************************************************************
20 *
21 * NOTE: DOXYGEN is used to generate documentation for this file.
22 *
23 *******************************************************************************/
24 
25 /** @file file_buffer.c
26 * File Buffer Functions
27 */
28 
29 #include <errno.h>
30 
31 #include "armutils.h"
32 
33 /**
34 * Free memory used by a FileBuffer structure.
35 *
36 * @param fbuf pointer to the FileBuffer structure
37 */
39 {
40  if (fbuf) {
41  if (fbuf->full_path) free((void *)fbuf->full_path);
42  if (fbuf->stats) free(fbuf->stats);
43  if (fbuf->data) free(fbuf->data);
44  if (fbuf->lines) free(fbuf->lines);
45  free(fbuf);
46  }
47 }
48 
49 /**
50 * Allocate memory for a FileBuffer structure.
51 *
52 * Error messages from this function are sent to the message handler
53 * (see msngr_init_log() and msngr_init_mail()).
54 *
55 * @retval fbuf pointer to the FileBuffer structure
56 * @retval NULL if a memory allocation error occurred
57 */
59 {
60  FileBuffer *fbuf = calloc(1, sizeof(FileBuffer));
61 
62  if (!fbuf) {
63 
65  "Could not allocate memory for new FileBuffer structure\n");
66 
67  return((FileBuffer *)NULL);
68  }
69 
70  fbuf->full_path = calloc(PATH_MAX, sizeof(char));
71  if (!fbuf->full_path) {
72 
74  "Could not allocate memory for new FileBuffer structure\n");
75 
76  free(fbuf);
77  return((FileBuffer *)NULL);
78  }
79 
80  fbuf->stats = calloc(1, sizeof(struct stat));
81  if (!fbuf->stats) {
82 
84  "Could not allocate memory for new FileBuffer structure\n");
85 
86  free((void *)fbuf->full_path);
87  free(fbuf);
88  return((FileBuffer *)NULL);
89  }
90 
91  return(fbuf);
92 }
93 
94 /**
95 * Read a file into a FileBuffer.
96 *
97 * The in memory copy of the file can be accessed via the 'data' member of
98 * the FileBuffer structure. This memory is managed by the FileBuffer
99 * structure and should not be freed by the calling process. It will be
100 * freed when file_buffer_destroy() is called.
101 *
102 * Using the same FileBuffer to read additional files will reused the
103 * previously allocated memory, reallocating more as necessary.
104 *
105 * \b Example:
106 * \code
107 *
108 * const char *full_path = "/full/path/to/file";
109 * int errcode = 0;
110 *
111 * FileBuffer *fbuf;
112 * int nlines;
113 * char **lines;
114 * int status;
115 *
116 * char *linep;
117 * int li;
118 *
119 * if (!(fbuf = file_buffer_init())) {
120 * return(errcode);
121 * }
122 *
123 * if (!file_buffer_read(fbuf, full_path, NULL)) {
124 * return(errcode);
125 * }
126 *
127 * if (!file_buffer_split_lines(fbuf, &nlines, &lines)) {
128 * return(errcode);
129 * }
130 *
131 * for (li = 0; li < nlines; ++li) {
132 * linep = lines[li];
133 * printf("%s\n", linep);
134 * }
135 *
136 * \endcode
137 *
138 * Error messages from this function are sent to the message handler
139 * (see msngr_init_log() and msngr_init_mail()).
140 *
141 * @param fbuf pointer to the FileBuffer structure
142 * @param full_path full path to the input file
143 * @param data output: fbuf->data if not NULL
144 *
145 * @retval 1 if successful (or zero length file)
146 * @retval 0 if an errror occurred
147 */
149  FileBuffer *fbuf,
150  const char *full_path,
151  char **data)
152 {
153  char *new_data;
154  size_t length;
155  size_t nread;
156  FILE *fp;
157 
158  if (data) *data = (char *)NULL;
159 
160  /* Clear structure data for new file read */
161 
162  fbuf->length = 0;
163  fbuf->nlines = 0;
164 
165  if (fbuf->data) fbuf->data[0] = '\0';
166  if (fbuf->lines) fbuf->lines[0] = (char *)NULL;
167 
168  /* Set the full_path in the FileBuffer */
169 
170  strncpy((char *)fbuf->full_path, full_path, PATH_MAX);
171 
172  /* Get file size */
173 
174  if (stat(full_path, fbuf->stats) != 0) {
175 
177  "Could not read file: %s\n"
178  " -> %s\n",
179  full_path, strerror(errno));
180 
181  return(0);
182  }
183 
184  /* Resize buffer if necessary */
185 
186  length = fbuf->stats->st_size;
187  if (length == 0) {
188  return(1);
189  }
190 
191  if (length >= fbuf->data_nalloced) {
192 
193  new_data = realloc(fbuf->data, (length + 1) * sizeof(char));
194  if (!new_data) {
195 
197  "Could not read file: %s\n"
198  " -> memory allocation error",
199  full_path);
200 
201  return(0);
202  }
203 
204  fbuf->data_nalloced = length + 1;
205  fbuf->data = new_data;
206  }
207 
208  /* Read in the entire file */
209 
210  fp = fopen(full_path, "r");
211  if (!fp) {
212 
214  "Could not open file: %s\n"
215  " -> %s\n",
216  full_path, strerror(errno));
217 
218  return(0);
219  }
220 
221  nread = fread(fbuf->data, 1, length, fp);
222 
223  if (ferror(fp)) {
224 
226  "Could not read file: %s\n"
227  " -> %s\n",
228  full_path, strerror(errno));
229 
230  fclose(fp);
231  return(0);
232  }
233 
234  fclose(fp);
235 
236  if (nread != length) {
237 
239  "Could not read file: %s\n"
240  " -> number of bytes read (%d) != file size %d bytes\n",
241  full_path, nread, length);
242 
243  return(0);
244  }
245 
246  fbuf->length = length;
247  fbuf->data[fbuf->length] = '\0';
248 
249  if (data) *data = fbuf->data;
250 
251  return(1);
252 }
253 
254 /**
255 * Create array of line pointers into a FileBuffer.
256 *
257 * This function will replace newline characters '\n' with string terminator
258 * characters '\0' and create an array of pointers to each line in the buffer.
259 * The output fbuf->lines array will be null terminated.
260 *
261 * The memory used by the returned array of line pointers is managed by the
262 * FileBuffer structure and should not be freed by the calling process.
263 *
264 * The number of lines and line pointers can also be accessed using the
265 * 'nlines' and 'lines' members of the FileBuffer structure.
266 *
267 * \b Example:
268 * \code
269 * \endcode
270 *
271 * Error messages from this function are sent to the message handler
272 * (see msngr_init_log() and msngr_init_mail()).
273 *
274 * @param fbuf pointer to the FileBuffer structure
275 * @param nlines output: fbuf->nlines if not NULL
276 * @param lines output: fbuf->lines if not NULL
277 *
278 * @retval 1 if successful
279 * @retval 0 if a memory allocation error occurred
280 */
282  FileBuffer *fbuf,
283  int *nlines,
284  char ***lines)
285 {
286  char **new_lines;
287  char *chrp;
288  size_t count;
289 
290  if (fbuf->nlines) {
291 
292  if (nlines) *nlines = fbuf->nlines;
293  if (lines) *lines = fbuf->lines;
294 
295  return(1);
296  }
297 
298  if (nlines) *nlines = 0;
299  if (lines) *lines = (char **)NULL;
300 
301  /* Count the number of new line characters */
302 
303  chrp = fbuf->data;
304  count = 1;
305 
306  while( (chrp = strchr(chrp, '\n')) ) {
307  count += 1;
308  chrp += 1;
309  }
310 
311  /* Allocate memory for the array of line pointesr */
312 
313  if (count >= fbuf->lines_nalloced) {
314 
315  new_lines = realloc(fbuf->lines, (count+1) * sizeof(char *));
316  if (!new_lines) {
317 
319  "Could not allocate memory for FileBuffer line pointers\n");
320 
321  return(0);
322  }
323 
324  fbuf->lines = new_lines;
325  fbuf->lines_nalloced = count + 1;
326  }
327 
328  /* Create array of line pointers */
329 
330  fbuf->lines[0] = chrp = fbuf->data;
331  fbuf->nlines = 1;
332 
333  while( (chrp = strchr(chrp, '\n')) ) {
334  *chrp = '\0';
335  fbuf->lines[fbuf->nlines++] = ++chrp;
336  }
337 
338  fbuf->lines[fbuf->nlines] = (char *)NULL;
339 
340  if (nlines) *nlines = fbuf->nlines;
341  if (lines) *lines = fbuf->lines;
342 
343  return(1);
344 }