gui_string_util.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /* gui_string_util.c - String utilities used by the GUI system. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2008,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB 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 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/gui_string_util.h>
  20. #include <grub/types.h>
  21. #include <grub/misc.h>
  22. #include <grub/mm.h>
  23. /* Create a new NUL-terminated string on the heap as a substring of BUF.
  24. The range of buf included is the half-open interval [START,END).
  25. The index START is inclusive, END is exclusive. */
  26. char *
  27. grub_new_substring (const char *buf,
  28. grub_size_t start, grub_size_t end)
  29. {
  30. if (end < start)
  31. return 0;
  32. grub_size_t len = end - start;
  33. char *s = grub_malloc (len + 1);
  34. if (! s)
  35. return 0;
  36. grub_memcpy (s, buf + start, len);
  37. s[len] = '\0';
  38. return s;
  39. }
  40. /* Eliminate "." and ".." path elements from PATH. A new heap-allocated
  41. string is returned. */
  42. static char *
  43. canonicalize_path (const char *path)
  44. {
  45. int i;
  46. const char *p;
  47. char *newpath = 0;
  48. /* Count the path components in path. */
  49. int components = 1;
  50. for (p = path; *p; p++)
  51. if (*p == '/')
  52. components++;
  53. char **path_array = grub_malloc (components * sizeof (*path_array));
  54. if (! path_array)
  55. return 0;
  56. /* Initialize array elements to NULL pointers; in case once of the
  57. allocations fails, the cleanup code can just call grub_free() for all
  58. pointers in the array. */
  59. for (i = 0; i < components; i++)
  60. path_array[i] = 0;
  61. /* Parse the path into path_array. */
  62. p = path;
  63. for (i = 0; i < components && p; i++)
  64. {
  65. /* Find the end of the path element. */
  66. const char *end = grub_strchr (p, '/');
  67. if (!end)
  68. end = p + grub_strlen (p);
  69. /* Copy the element. */
  70. path_array[i] = grub_new_substring (p, 0, end - p);
  71. if (! path_array[i])
  72. goto cleanup;
  73. /* Advance p to point to the start of the next element, or NULL. */
  74. if (*end)
  75. p = end + 1;
  76. else
  77. p = 0;
  78. }
  79. /* Eliminate '.' and '..' elements from the path array. */
  80. int newpath_length = 0;
  81. for (i = components - 1; i >= 0; --i)
  82. {
  83. if (! grub_strcmp (path_array[i], "."))
  84. {
  85. grub_free (path_array[i]);
  86. path_array[i] = 0;
  87. }
  88. else if (! grub_strcmp (path_array[i], "..")
  89. && i > 0)
  90. {
  91. /* Delete the '..' and the prior path element. */
  92. grub_free (path_array[i]);
  93. path_array[i] = 0;
  94. --i;
  95. grub_free (path_array[i]);
  96. path_array[i] = 0;
  97. }
  98. else
  99. {
  100. newpath_length += grub_strlen (path_array[i]) + 1;
  101. }
  102. }
  103. /* Construct a new path string. */
  104. newpath = grub_malloc (newpath_length + 1);
  105. if (! newpath)
  106. goto cleanup;
  107. newpath[0] = '\0';
  108. char *newpath_end = newpath;
  109. int first = 1;
  110. for (i = 0; i < components; i++)
  111. {
  112. char *element = path_array[i];
  113. if (element)
  114. {
  115. /* For all components but the first, prefix with a slash. */
  116. if (! first)
  117. newpath_end = grub_stpcpy (newpath_end, "/");
  118. newpath_end = grub_stpcpy (newpath_end, element);
  119. first = 0;
  120. }
  121. }
  122. cleanup:
  123. for (i = 0; i < components; i++)
  124. grub_free (path_array[i]);
  125. grub_free (path_array);
  126. return newpath;
  127. }
  128. /* Return a new heap-allocated string representing to absolute path
  129. to the file referred to by PATH. If PATH is an absolute path, then
  130. the returned path is a copy of PATH. If PATH is a relative path, then
  131. BASE is with PATH used to construct the absolute path. */
  132. char *
  133. grub_resolve_relative_path (const char *base, const char *path)
  134. {
  135. char *abspath;
  136. char *canonpath;
  137. char *p;
  138. grub_size_t l;
  139. /* If PATH is an absolute path, then just use it as is. */
  140. if (path[0] == '/' || path[0] == '(')
  141. return canonicalize_path (path);
  142. abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 3);
  143. if (! abspath)
  144. return 0;
  145. /* Concatenate BASE and PATH. */
  146. p = grub_stpcpy (abspath, base);
  147. l = grub_strlen (abspath);
  148. if (l == 0 || abspath[l-1] != '/')
  149. {
  150. *p = '/';
  151. p++;
  152. *p = 0;
  153. }
  154. grub_stpcpy (p, path);
  155. canonpath = canonicalize_path (abspath);
  156. if (! canonpath)
  157. return abspath;
  158. grub_free (abspath);
  159. return canonpath;
  160. }
  161. /* Get the path of the directory where the file at FILE_PATH is located.
  162. FILE_PATH should refer to a file, not a directory. The returned path
  163. includes a trailing slash.
  164. This does not handle GRUB "(hd0,0)" paths properly yet since it only
  165. looks at slashes. */
  166. char *
  167. grub_get_dirname (const char *file_path)
  168. {
  169. int i;
  170. int last_slash;
  171. last_slash = -1;
  172. for (i = grub_strlen (file_path) - 1; i >= 0; --i)
  173. {
  174. if (file_path[i] == '/')
  175. {
  176. last_slash = i;
  177. break;
  178. }
  179. }
  180. if (last_slash == -1)
  181. return grub_strdup ("/");
  182. return grub_new_substring (file_path, 0, last_slash + 1);
  183. }
  184. static __inline int
  185. my_isxdigit (char c)
  186. {
  187. return ((c >= '0' && c <= '9')
  188. || (c >= 'a' && c <= 'f')
  189. || (c >= 'A' && c <= 'F'));
  190. }
  191. static int
  192. parse_hex_color_component (const char *s, unsigned start, unsigned end)
  193. {
  194. unsigned len;
  195. char buf[3];
  196. len = end - start;
  197. /* Check the limits so we don't overrun the buffer. */
  198. if (len < 1 || len > 2)
  199. return 0;
  200. if (len == 1)
  201. {
  202. buf[0] = s[start]; /* Get the first and only hex digit. */
  203. buf[1] = buf[0]; /* Duplicate the hex digit. */
  204. }
  205. else if (len == 2)
  206. {
  207. buf[0] = s[start];
  208. buf[1] = s[start + 1];
  209. }
  210. buf[2] = '\0';
  211. return grub_strtoul (buf, 0, 16);
  212. }
  213. /* Parse a color string of the form "r, g, b", "#RGB", "#RGBA",
  214. "#RRGGBB", or "#RRGGBBAA". */
  215. grub_err_t
  216. grub_gui_parse_color (const char *s, grub_gui_color_t *color)
  217. {
  218. grub_gui_color_t c;
  219. /* Skip whitespace. */
  220. while (*s && grub_isspace (*s))
  221. s++;
  222. if (*s == '#')
  223. {
  224. /* HTML-style. Number if hex digits:
  225. [6] #RRGGBB [3] #RGB
  226. [8] #RRGGBBAA [4] #RGBA */
  227. s++; /* Skip the '#'. */
  228. /* Count the hexits to determine the format. */
  229. int hexits = 0;
  230. const char *end = s;
  231. while (my_isxdigit (*end))
  232. {
  233. end++;
  234. hexits++;
  235. }
  236. /* Parse the color components based on the format. */
  237. if (hexits == 3 || hexits == 4)
  238. {
  239. c.red = parse_hex_color_component (s, 0, 1);
  240. c.green = parse_hex_color_component (s, 1, 2);
  241. c.blue = parse_hex_color_component (s, 2, 3);
  242. if (hexits == 4)
  243. c.alpha = parse_hex_color_component (s, 3, 4);
  244. else
  245. c.alpha = 255;
  246. }
  247. else if (hexits == 6 || hexits == 8)
  248. {
  249. c.red = parse_hex_color_component (s, 0, 2);
  250. c.green = parse_hex_color_component (s, 2, 4);
  251. c.blue = parse_hex_color_component (s, 4, 6);
  252. if (hexits == 8)
  253. c.alpha = parse_hex_color_component (s, 6, 8);
  254. else
  255. c.alpha = 255;
  256. }
  257. else
  258. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  259. "invalid HTML-type color string `%s'", s);
  260. }
  261. else if (grub_isdigit (*s))
  262. {
  263. /* Comma separated decimal values. */
  264. c.red = grub_strtoul (s, 0, 0);
  265. if ((s = grub_strchr (s, ',')) == 0)
  266. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  267. "missing 1st comma separator in color `%s'", s);
  268. s++;
  269. c.green = grub_strtoul (s, 0, 0);
  270. if ((s = grub_strchr (s, ',')) == 0)
  271. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  272. "missing 2nd comma separator in color `%s'", s);
  273. s++;
  274. c.blue = grub_strtoul (s, 0, 0);
  275. if ((s = grub_strchr (s, ',')) == 0)
  276. c.alpha = 255;
  277. else
  278. {
  279. s++;
  280. c.alpha = grub_strtoul (s, 0, 0);
  281. }
  282. }
  283. else
  284. {
  285. if (! grub_gui_get_named_color (s, &c))
  286. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  287. "invalid named color `%s'", s);
  288. }
  289. if (grub_errno == GRUB_ERR_NONE)
  290. *color = c;
  291. return grub_errno;
  292. }