SDDSlib
Loading...
Searching...
No Matches
filestat.c
Go to the documentation of this file.
1/**
2 * @file filestat.c
3 * @brief Utility functions for handling file links and retrieving file information.
4 *
5 * @copyright
6 * - (c) 2002 The University of Chicago, as Operator of Argonne National Laboratory.
7 * - (c) 2002 The Regents of the University of California, as Operator of Los Alamos National Laboratory.
8 *
9 * @license
10 * This file is distributed under the terms of the Software License Agreement
11 * found in the file LICENSE included with this distribution.
12 *
13 * @author H. Shang, M. Borland, R. Soliday
14 */
15
16#if !defined(_WIN32)
17# include <unistd.h>
18#endif
19#include "mdb.h"
20
21 /**
22 * @brief Retrieves the leading directories from a given path.
23 *
24 * Allocates memory for the leading directories part of the provided PATH.
25 * If memory allocation fails, returns NULL. Assumes that any trailing
26 * slashes in PATH have already been removed. This function avoids using
27 * the `dirname` builtin to maintain consistent behavior across different
28 * environments.
29 *
30 * @param path The file path from which to extract the leading directories.
31 * @return A newly allocated string containing the leading directories, or NULL if out of memory.
32 */
33char *dir_name(const char *path) {
34 char *newpath = NULL;
35 const char *slash = NULL;
36 int length; /* Length of result, not including NUL. */
37
38 slash = strrchr(path, '/');
39 if (slash == 0) {
40 /* File is in the current directory. */
41 path = ".";
42 length = 1;
43 } else {
44 /* Remove any trailing slashes from the result. */
45 while (slash > path && *slash == '/')
46 --slash;
47 length = slash - path + 1;
48 }
49 newpath = (char *)malloc(length + 1);
50 if (newpath == 0)
51 return 0;
52 strncpy(newpath, path, length);
53 newpath[length] = 0;
54 return newpath;
55}
56
57 /**
58 * @brief Reads the first link of a file.
59 *
60 * Returns the first link of the specified file. If the file is not a link
61 * or if an error occurs (including unsupported platforms), returns NULL.
62 * On successful retrieval, the returned string is dynamically allocated
63 * and should be freed by the caller.
64 *
65 * @param filename The name of the file to read the link from.
66 * @return A newly allocated string containing the link target, or NULL if not a link or on error.
67 */
68char *read_file_link(const char *filename) {
69#if defined(_WIN32) || defined(vxWorks)
70 return (NULL);
71#else
72 int size = 100, nchars = -1;
73 char *tmpbuf = NULL, *dir = NULL, *tempname = NULL;
74 tmpbuf = (char *)calloc(size, sizeof(char));
75 while (1) {
76 nchars = readlink(filename, tmpbuf, size);
77 if (nchars < 0) {
78 free(tmpbuf);
79 return NULL;
80 }
81 if (nchars < size) {
82 if (tmpbuf[0] == '/')
83 return tmpbuf;
84 else {
85 dir = dir_name(filename);
86 tempname = (char *)malloc(sizeof(char) * (strlen(filename) + strlen(tmpbuf) + 2));
87 tempname[0] = 0;
88 strcat(tempname, dir);
89 strcat(tempname, "/");
90 strcat(tempname, tmpbuf);
91 free(tmpbuf);
92 free(dir);
93 return tempname;
94 }
95 }
96 size *= 2;
97 tmpbuf = (char *)realloc(tmpbuf, sizeof(char) * size);
98 }
99#endif
100}
101
102 /**
103 * @brief Retrieves the last link in a chain of symbolic links.
104 *
105 * Traverses the chain of symbolic links starting from the given filename
106 * and returns the name of the last link that directly points to the final
107 * target file. If the file is not a link, returns the original filename.
108 *
109 * @param filename The starting file name to resolve the last link from.
110 * @return The name of the last link in the chain, or the original filename if not a link.
111 */
112const char *read_file_lastlink(const char *filename) {
113 char *linkname = NULL;
114 const char *lastlink = NULL;
115 lastlink = filename;
116 while ((linkname = read_file_link(filename)) != NULL) {
117 lastlink = filename;
118 filename = linkname;
119 }
120 return lastlink;
121}
122
123 /**
124 * @brief Resolves the final target file that a symbolic link points to.
125 *
126 * Follows the chain of symbolic links starting from the given filename and
127 * returns the name of the final target file. The returned string is dynamically
128 * allocated and should be freed by the caller. If the filename is not a link,
129 * returns NULL.
130 *
131 * @param filename The starting file name to resolve to the final target.
132 * @return A newly allocated string containing the final target file name, or NULL if not a link.
133 */
134char *read_last_link_to_file(const char *filename) {
135 char *linkname = NULL;
136 char *tmpname = NULL;
137
138 if ((linkname = read_file_link(filename)) == NULL)
139 return NULL;
140 tmpname = (char *)calloc(1024, sizeof(char));
141 do {
142 strcpy_ss(tmpname, linkname);
143 free(linkname);
144 linkname = read_file_link(tmpname);
145 } while (linkname != NULL);
146 return tmpname;
147}
148
149 /**
150 * @brief Retrieves the file status of a given file or its final link target.
151 *
152 * Obtains the file status information for the specified filename. If the file
153 * is a symbolic link and a final_file is provided, it retrieves the status of
154 * the final target file. If the file does not exist or an error occurs during
155 * the retrieval, an error message is printed to stderr.
156 *
157 * @param filename The name of the file to retrieve the status for.
158 * @param final_file Optional parameter specifying the final target file if filename is a link.
159 * @param filestat Pointer to a struct stat where the file status will be stored.
160 * @return 0 on success, 1 on failure.
161 */
162long get_file_stat(const char *filename, const char *final_file, struct stat *filestat) {
163 const char *input = NULL;
164 if (!fexists(filename)) {
165 fprintf(stderr, "%s file does not exist, unable to get the state of it!\n", filename);
166 return (1);
167 }
168 input = filename;
169 if (final_file)
170 input = final_file;
171 if (stat(input, filestat) != 0) {
172 fprintf(stderr, "Problem getting state of file %s\n", input);
173 return (1);
174 }
175 return 0;
176}
177
178 /**
179 * @brief Checks if a file has been modified.
180 *
181 * Determines whether the specified input file has been modified by comparing
182 * its current state against a previously recorded state. If the file is a symbolic
183 * link, it checks the final target file for modifications. The function updates
184 * the final_file pointer if the link target has changed.
185 *
186 * @param inputfile The path to the input file to check for modifications.
187 * @param final_file Pointer to a string containing the final target file name. May be updated.
188 * @param input_stat Pointer to a struct stat containing the previous state of the file.
189 * @return 1 if the file has been modified or the link target has changed, 0 otherwise.
190 */
191long file_is_modified(const char *inputfile, char **final_file, struct stat *input_stat) {
192 struct stat filestat;
193 char *tempfile = NULL;
194 const char *tmpinput = NULL;
195
196 if (!fexists(inputfile)) {
197 fprintf(stderr, "%s file does not exist!\n", inputfile);
198 return (1);
199 }
200 if (*final_file && !fexists(*final_file)) {
201 fprintf(stderr, "linked file %s of inputfile %s does not exist!\n", *final_file, inputfile);
202 return (1);
203 }
204 if (!input_stat) {
205 fprintf(stderr, "The previous state of file %s is not known.\n", inputfile);
206 return (1);
207 }
208
209 tempfile = read_last_link_to_file(inputfile);
210
211 /*the final link name changed */
212 if (tempfile && *final_file && strcmp(tempfile, *final_file)) {
213 if (*final_file)
214 free(*final_file);
215 *final_file = tempfile;
216 return 1;
217 }
218 if ((tempfile && !(*final_file)) || (!tempfile && *final_file)) {
219 if (*final_file)
220 free(*final_file);
221 *final_file = tempfile;
222 return 1;
223 }
224 /* the final link name did not change, check if the file state changed */
225 if (tempfile)
226 free(tempfile);
227 if (*final_file)
228 tmpinput = *final_file;
229 else
230 tmpinput = inputfile;
231 filestat = *input_stat;
232 if (stat(tmpinput, input_stat) != 0) {
233 fprintf(stderr, "Problem getting modification time for %s\n", tmpinput);
234 exit(1);
235 }
236 if (input_stat->st_ctime != filestat.st_ctime) {
237 /* file state is changed */
238 return 1;
239 }
240 return 0;
241}
long fexists(const char *filename)
Checks if a file exists.
Definition fexists.c:27
long get_file_stat(const char *filename, const char *final_file, struct stat *filestat)
Retrieves the file status of a given file or its final link target.
Definition filestat.c:162
char * read_file_link(const char *filename)
Reads the first link of a file.
Definition filestat.c:68
const char * read_file_lastlink(const char *filename)
Retrieves the last link in a chain of symbolic links.
Definition filestat.c:112
long file_is_modified(const char *inputfile, char **final_file, struct stat *input_stat)
Checks if a file has been modified.
Definition filestat.c:191
char * read_last_link_to_file(const char *filename)
Resolves the final target file that a symbolic link points to.
Definition filestat.c:134
char * dir_name(const char *path)
Retrieves the leading directories from a given path.
Definition filestat.c:33
char * strcpy_ss(char *dest, const char *src)
Safely copies a string, handling memory overlap.
Definition str_copy.c:34
char * tmpname(char *s)
Supplies a unique temporary filename.
Definition tmpname.c:34