1 /*
2  *  util-common.c
3  *
4  *  Copyright (c) 2006-2018 Pacman Development Team <pacman-dev@archlinux.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <ctype.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "util-common.h"
26 
27 
28 /** Parse the basename of a program from a path.
29 * @param path path to parse basename from
30 *
31 * @return everything following the final '/'
32 */
mbasename(const char * path)33 const char *mbasename(const char *path)
34 {
35 	const char *last = strrchr(path, '/');
36 	if(last) {
37 		return last + 1;
38 	}
39 	return path;
40 }
41 
42 /** Parse the dirname of a program from a path.
43 * The path returned should be freed.
44 * @param path path to parse dirname from
45 *
46 * @return everything preceding the final '/'
47 */
mdirname(const char * path)48 char *mdirname(const char *path)
49 {
50 	char *ret, *last;
51 
52 	/* null or empty path */
53 	if(path == NULL || *path == '\0') {
54 		return strdup(".");
55 	}
56 
57 	if((ret = strdup(path)) == NULL) {
58 		return NULL;
59 	}
60 
61 	last = strrchr(ret, '/');
62 
63 	if(last != NULL) {
64 		/* we found a '/', so terminate our string */
65 		if(last == ret) {
66 			/* return "/" for root */
67 			last++;
68 		}
69 		*last = '\0';
70 		return ret;
71 	}
72 
73 	/* no slash found */
74 	free(ret);
75 	return strdup(".");
76 }
77 
78 /** lstat wrapper that treats /path/dirsymlink/ the same as /path/dirsymlink.
79  * Linux lstat follows POSIX semantics and still performs a dereference on
80  * the first, and for uses of lstat in libalpm this is not what we want.
81  * @param path path to file to lstat
82  * @param buf structure to fill with stat information
83  * @return the return code from lstat
84  */
llstat(char * path,struct stat * buf)85 int llstat(char *path, struct stat *buf)
86 {
87 	int ret;
88 	char *c = NULL;
89 	size_t len = strlen(path);
90 
91 	while(len > 1 && path[len - 1] == '/') {
92 		--len;
93 		c = path + len;
94 	}
95 
96 	if(c) {
97 		*c = '\0';
98 		ret = lstat(path, buf);
99 		*c = '/';
100 	} else {
101 		ret = lstat(path, buf);
102 	}
103 
104 	return ret;
105 }
106 
107 /** Wrapper around fgets() which properly handles EINTR
108  * @param s string to read into
109  * @param size maximum length to read
110  * @param stream stream to read from
111  * @return value returned by fgets()
112  */
safe_fgets(char * s,int size,FILE * stream)113 char *safe_fgets(char *s, int size, FILE *stream)
114 {
115 	char *ret;
116 	int errno_save = errno, ferror_save = ferror(stream);
117 	while((ret = fgets(s, size, stream)) == NULL && !feof(stream)) {
118 		if(errno == EINTR) {
119 			/* clear any errors we set and try again */
120 			errno = errno_save;
121 			if(!ferror_save) {
122 				clearerr(stream);
123 			}
124 		} else {
125 			break;
126 		}
127 	}
128 	return ret;
129 }
130 
131 /* Trim whitespace and newlines from a string
132  */
strtrim(char * str)133 size_t strtrim(char *str)
134 {
135 	char *end, *pch = str;
136 
137 	if(str == NULL || *str == '\0') {
138 		/* string is empty, so we're done. */
139 		return 0;
140 	}
141 
142 	while(isspace((unsigned char)*pch)) {
143 		pch++;
144 	}
145 	if(pch != str) {
146 		size_t len = strlen(pch);
147 		/* check if there wasn't anything but whitespace in the string. */
148 		if(len == 0) {
149 			*str = '\0';
150 			return 0;
151 		}
152 		memmove(str, pch, len + 1);
153 		pch = str;
154 	}
155 
156 	end = (str + strlen(str) - 1);
157 	while(isspace((unsigned char)*end)) {
158 		end--;
159 	}
160 	*++end = '\0';
161 
162 	return end - pch;
163 }
164 
165 #ifndef HAVE_STRNLEN
166 /* A quick and dirty implementation derived from glibc */
167 /** Determines the length of a fixed-size string.
168  * @param s string to be measured
169  * @param max maximum number of characters to search for the string end
170  * @return length of s or max, whichever is smaller
171  */
strnlen(const char * s,size_t max)172 static size_t strnlen(const char *s, size_t max)
173 {
174 	register const char *p;
175 	for(p = s; *p && max--; ++p);
176 	return (p - s);
177 }
178 #endif
179 
180 #ifndef HAVE_STRNDUP
181 /** Copies a string.
182  * Returned string needs to be freed
183  * @param s string to be copied
184  * @param n maximum number of characters to copy
185  * @return pointer to the new string on success, NULL on error
186  */
strndup(const char * s,size_t n)187 char *strndup(const char *s, size_t n)
188 {
189 	size_t len = strnlen(s, n);
190 	char *new = (char *) malloc(len + 1);
191 
192 	if(new == NULL) {
193 		return NULL;
194 	}
195 
196 	new[len] = '\0';
197 	return (char *)memcpy(new, s, len);
198 }
199 #endif
200