yuarel.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * yuarel.c
  3. *
  4. * Created on: 2022/12/9
  5. * Author: folus
  6. */
  7. #include "yuarel.h"
  8. /**
  9. * Parse a non null terminated string into an integer.
  10. *
  11. * str: the string containing the number.
  12. * len: Number of characters to parse.
  13. */
  14. static inline int natoi(const char *str, size_t len)
  15. {
  16. int i, r = 0;
  17. for (i = 0; i < len; i++) {
  18. r *= 10;
  19. r += str[i] - '0';
  20. }
  21. return r;
  22. }
  23. /**
  24. * Check if a URL is relative (no scheme and hostname).
  25. *
  26. * url: the string containing the URL to check.
  27. *
  28. * Returns 1 if relative, otherwise 0.
  29. */
  30. static inline int is_relative(const char *url)
  31. {
  32. return (*url == '/') ? 1 : 0;
  33. }
  34. /**
  35. * Parse the scheme of a URL by inserting a null terminator after the scheme.
  36. *
  37. * str: the string containing the URL to parse. Will be modified.
  38. *
  39. * Returns a pointer to the hostname on success, otherwise NULL.
  40. */
  41. static inline char *parse_scheme(char *str)
  42. {
  43. char *s;
  44. /* If not found or first in string, return error */
  45. s = strchr(str, ':');
  46. if (s == NULL || s == str) {
  47. return NULL;
  48. }
  49. /* If not followed by two slashes, return error */
  50. if (s[1] == '\0' || s[1] != '/' || s[2] == '\0' || s[2] != '/') {
  51. return NULL;
  52. }
  53. *s = '\0'; // Replace ':' with NULL
  54. return s + 3;
  55. }
  56. /**
  57. * Find a character in a string, replace it with '\0' and return the next
  58. * character in the string.
  59. *
  60. * str: the string to search in.
  61. * find: the character to search for.
  62. *
  63. * Returns a pointer to the character after the one to search for. If not
  64. * found, NULL is returned.
  65. */
  66. static inline char *find_and_terminate(char *str, char find)
  67. {
  68. str = strchr(str, find);
  69. if (NULL == str) {
  70. return NULL;
  71. }
  72. *str = '\0';
  73. return str + 1;
  74. }
  75. /* Yes, the following functions could be implemented as preprocessor macros
  76. instead of inline functions, but I think that this approach will be more
  77. clean in this case. */
  78. static inline char *find_fragment(char *str)
  79. {
  80. return find_and_terminate(str, '#');
  81. }
  82. static inline char *find_query(char *str)
  83. {
  84. return find_and_terminate(str, '?');
  85. }
  86. static inline char *find_path(char *str)
  87. {
  88. return find_and_terminate(str, '/');
  89. }
  90. /**
  91. * Parse a URL string to a struct.
  92. *
  93. * url: pointer to the struct where to store the parsed URL parts.
  94. * u: the string containing the URL to be parsed.
  95. *
  96. * Returns 0 on success, otherwise -1.
  97. */
  98. int yuarel_parse(struct yuarel *url, char *u)
  99. {
  100. if (NULL == url || NULL == u) {
  101. return -1;
  102. }
  103. memset(url, 0, sizeof (struct yuarel));
  104. /* (Fragment) */
  105. url->fragment = find_fragment(u);
  106. /* (Query) */
  107. url->query = find_query(u);
  108. /* Relative URL? Parse scheme and hostname */
  109. if (!is_relative(u)) {
  110. /* Scheme */
  111. url->scheme = u;
  112. u = parse_scheme(u);
  113. if (u == NULL) {
  114. return -1;
  115. }
  116. /* Host */
  117. if ('\0' == *u) {
  118. return -1;
  119. }
  120. url->host = u;
  121. /* (Path) */
  122. url->path = find_path(u);
  123. /* (Credentials) */
  124. u = strchr(url->host, '@');
  125. if (NULL != u) {
  126. /* Missing credentials? */
  127. if (u == url->host) {
  128. return -1;
  129. }
  130. url->username = url->host;
  131. url->host = u + 1;
  132. *u = '\0';
  133. u = strchr(url->username, ':');
  134. if (NULL == u) {
  135. return -1;
  136. }
  137. url->password = u + 1;
  138. *u = '\0';
  139. }
  140. /* Missing hostname? */
  141. if ('\0' == *url->host) {
  142. return -1;
  143. }
  144. /* (Port) */
  145. u = strchr(url->host, ':');
  146. if (NULL != u && (NULL == url->path || u < url->path)) {
  147. *(u++) = '\0';
  148. if ('\0' == *u) {
  149. return -1;
  150. }
  151. if (url->path) {
  152. url->port = natoi(u, url->path - u - 1);
  153. } else {
  154. url->port = atoi(u);
  155. }
  156. }
  157. /* Missing hostname? */
  158. if ('\0' == *url->host) {
  159. return -1;
  160. }
  161. } else {
  162. /* (Path) */
  163. url->path = find_path(u);
  164. }
  165. return 0;
  166. }
  167. /**
  168. * Split a path into several strings.
  169. *
  170. * No data is copied, the slashed are used as null terminators and then
  171. * pointers to each path part will be stored in **parts. Double slashes will be
  172. * treated as one.
  173. *
  174. * path: the path to split.
  175. * parts: a pointer to an array of (char *) where to store the result.
  176. * max_parts: max number of parts to parse.
  177. */
  178. int
  179. yuarel_split_path(char *path, char **parts, int max_parts)
  180. {
  181. int i = 0;
  182. if (NULL == path || '\0' == *path) {
  183. return -1;
  184. }
  185. do {
  186. /* Forward to after slashes */
  187. while (*path == '/') path++;
  188. if ('\0' == *path) {
  189. break;
  190. }
  191. parts[i++] = path;
  192. path = strchr(path, '/');
  193. if (NULL == path) {
  194. break;
  195. }
  196. *(path++) = '\0';
  197. } while (i < max_parts);
  198. return i;
  199. }
  200. int
  201. yuarel_parse_query(char *query, char delimiter, struct yuarel_param *params, int max_params)
  202. {
  203. int i = 0;
  204. if (NULL == query || '\0' == *query) {
  205. return -1;
  206. }
  207. params[i++].key = query;
  208. while (i < max_params && NULL != (query = strchr(query, delimiter))) {
  209. *query = '\0';
  210. params[i].key = ++query;
  211. params[i].val = NULL;
  212. /* Go back and split previous param */
  213. if (i > 0) {
  214. if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
  215. *(params[i - 1].val)++ = '\0';
  216. }
  217. }
  218. i++;
  219. }
  220. /* Go back and split last param */
  221. if ((params[i - 1].val = strchr(params[i - 1].key, '=')) != NULL) {
  222. *(params[i - 1].val)++ = '\0';
  223. }
  224. return i;
  225. }