yuarel.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /**
  2. * Copyright (C) 2016,2017 Jack Engqvist Johansson
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in all
  12. * copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. #include <stdlib.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include "yuarel.h"
  26. /**
  27. * Parse a non null terminated string into an integer.
  28. *
  29. * str: the string containing the number.
  30. * len: Number of characters to parse.
  31. */
  32. static inline int
  33. natoi(const char *str, size_t len)
  34. {
  35. int i, r = 0;
  36. for (i = 0; i < len; i++) {
  37. r *= 10;
  38. r += str[i] - '0';
  39. }
  40. return r;
  41. }
  42. /**
  43. * Check if a URL is relative (no scheme and hostname).
  44. *
  45. * url: the string containing the URL to check.
  46. *
  47. * Returns 1 if relative, otherwise 0.
  48. */
  49. static inline int
  50. is_relative(const char *url)
  51. {
  52. return (*url == '/') ? 1 : 0;
  53. }
  54. /**
  55. * Parse the scheme of a URL by inserting a null terminator after the scheme.
  56. *
  57. * str: the string containing the URL to parse. Will be modified.
  58. *
  59. * Returns a pointer to the hostname on success, otherwise NULL.
  60. */
  61. static inline char *
  62. parse_scheme(char *str)
  63. {
  64. char *s;
  65. /* If not found or first in string, return error */
  66. s = strchr(str, ':');
  67. if (s == NULL || s == str) {
  68. return NULL;
  69. }
  70. /* If not followed by two slashes, return error */
  71. if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
  72. return NULL;
  73. }
  74. *s = '\0'; // Replace ':' with NULL
  75. return s + 3;
  76. }
  77. /**
  78. * Find a character in a string, replace it with '\0' and return the next
  79. * character in the string.
  80. *
  81. * str: the string to search in.
  82. * find: the character to search for.
  83. *
  84. * Returns a pointer to the character after the one to search for. If not
  85. * found, NULL is returned.
  86. */
  87. static inline char *
  88. find_and_terminate(char *str, char find)
  89. {
  90. str = strchr(str, find);
  91. if (NULL == str) {
  92. return NULL;
  93. }
  94. *str = '\0';
  95. return str + 1;
  96. }
  97. /* Yes, the following functions could be implemented as preprocessor macros
  98. instead of inline functions, but I think that this approach will be more
  99. clean in this case. */
  100. static inline char *
  101. find_fragment(char *str)
  102. {
  103. return find_and_terminate(str, '#');
  104. }
  105. static inline char *
  106. find_query(char *str)
  107. {
  108. return find_and_terminate(str, '?');
  109. }
  110. static inline char *
  111. find_path(char *str)
  112. {
  113. return find_and_terminate(str, '/');
  114. }
  115. /**
  116. * Parse a URL string to a struct.
  117. *
  118. * url: pointer to the struct where to store the parsed URL parts.
  119. * u: the string containing the URL to be parsed.
  120. *
  121. * Returns 0 on success, otherwise -1.
  122. */
  123. int
  124. yuarel_parse(struct yuarel *url, char *u)
  125. {
  126. if (NULL == url || NULL == u) {
  127. return -1;
  128. }
  129. memset(url, 0, sizeof (struct yuarel));
  130. /* (Fragment) */
  131. url->fragment = find_fragment(u);
  132. /* (Query) */
  133. url->query = find_query(u);
  134. /* Relative URL? Parse scheme and hostname */
  135. if (!is_relative(u)) {
  136. /* Scheme */
  137. url->scheme = u;
  138. u = parse_scheme(u);
  139. if (u == NULL) {
  140. return -1;
  141. }
  142. /* Host */
  143. if ('\0' == *u) {
  144. return -1;
  145. }
  146. url->host = u;
  147. /* (Path) */
  148. url->path = find_path(u);
  149. /* (Credentials) */
  150. u = strchr(url->host, '@');
  151. if (NULL != u) {
  152. /* Missing credentials? */
  153. if (u == url->host) {
  154. return -1;
  155. }
  156. url->username = url->host;
  157. url->host = u + 1;
  158. *u = '\0';
  159. u = strchr(url->username, ':');
  160. if (NULL == u) {
  161. return -1;
  162. }
  163. url->password = u + 1;
  164. *u = '\0';
  165. }
  166. /* Missing hostname? */
  167. if ('\0' == *url->host) {
  168. return -1;
  169. }
  170. /* (Port) */
  171. u = strchr(url->host, ':');
  172. if (NULL != u && (NULL == url->path || u < url->path)) {
  173. *(u++) = '\0';
  174. if ('\0' == *u) {
  175. return -1;
  176. }
  177. if (url->path) {
  178. url->port = natoi(u, url->path - u - 1);
  179. } else {
  180. url->port = atoi(u);
  181. }
  182. }
  183. /* Missing hostname? */
  184. if ('\0' == *url->host) {
  185. return -1;
  186. }
  187. } else {
  188. /* (Path) */
  189. url->path = find_path(u);
  190. }
  191. return 0;
  192. }
  193. /**
  194. * Split a path into several strings.
  195. *
  196. * No data is copied, the slashed are used as null terminators and then
  197. * pointers to each path part will be stored in **parts. Double slashes will be
  198. * treated as one.
  199. *
  200. * path: the path to split.
  201. * parts: a pointer to an array of (char *) where to store the result.
  202. * max_parts: max number of parts to parse.
  203. */
  204. int
  205. yuarel_split_path(char *path, char **parts, int max_parts)
  206. {
  207. int i = 0;
  208. if (NULL == path || '\0' == *path) {
  209. return -1;
  210. }
  211. do {
  212. /* Forward to after slashes */
  213. while (*path == '/') path++;
  214. if ('\0' == *path) {
  215. break;
  216. }
  217. parts[i++] = path;
  218. path = strchr(path, '/');
  219. if (NULL == path) {
  220. break;
  221. }
  222. *(path++) = '\0';
  223. } while (i < max_parts);
  224. return i;
  225. }
  226. int
  227. yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
  228. {
  229. int i = 0;
  230. if (NULL == query || '\0' == *query) {
  231. return -1;
  232. }
  233. params[i++].key = query;
  234. while (i < max_params && NULL != (query = strchr(query, delimiter))) {
  235. *query = '\0';
  236. params[i].key = ++query;
  237. params[i].val = NULL;
  238. /* Go back and split previous param */
  239. if (i > 0) {
  240. if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
  241. *(params[i - 1].val)++ = '\0';
  242. }
  243. }
  244. i++;
  245. }
  246. /* Go back and split last param */
  247. if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
  248. *(params[i - 1].val)++ = '\0';
  249. }
  250. return i;
  251. }