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