nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright © 2012 Red Hat, Inc |
||
3 | * |
||
4 | * This library is free software; you can redistribute it and/or |
||
5 | * modify it under the terms of the GNU Lesser General Public |
||
6 | * License as published by the Free Software Foundation; either |
||
7 | * version 2 of the licence, or (at your option) any later version. |
||
8 | * |
||
9 | * This library is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
12 | * Lesser General Public License for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU Lesser General Public |
||
15 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
16 | * |
||
17 | * Author: Matthias Clasen |
||
18 | */ |
||
19 | |||
20 | #include "config.h" |
||
21 | |||
22 | #include <stdlib.h> |
||
23 | #include <stdio.h> |
||
24 | |||
25 | #include <sys/types.h> |
||
26 | #include <sys/stat.h> |
||
27 | #include <fcntl.h> |
||
28 | #include <string.h> |
||
29 | #include <locale.h> |
||
30 | |||
31 | #ifdef HAVE_LIBELF |
||
32 | #include <libelf.h> |
||
33 | #include <gelf.h> |
||
34 | #include <sys/mman.h> |
||
35 | #endif |
||
36 | |||
37 | #include <gio/gio.h> |
||
38 | #include <glib/gstdio.h> |
||
39 | #include <gi18n.h> |
||
40 | |||
41 | #ifdef G_OS_WIN32 |
||
42 | #include "glib/glib-private.h" |
||
43 | #endif |
||
44 | |||
45 | /* GResource functions {{{1 */ |
||
46 | static GResource * |
||
47 | get_resource (const gchar *file) |
||
48 | { |
||
49 | gchar *content; |
||
50 | gsize size; |
||
51 | GResource *resource; |
||
52 | GBytes *data; |
||
53 | |||
54 | resource = NULL; |
||
55 | |||
56 | if (g_file_get_contents (file, &content, &size, NULL)) |
||
57 | { |
||
58 | data = g_bytes_new_take (content, size); |
||
59 | resource = g_resource_new_from_data (data, NULL); |
||
60 | g_bytes_unref (data); |
||
61 | } |
||
62 | |||
63 | return resource; |
||
64 | } |
||
65 | |||
66 | static void |
||
67 | list_resource (GResource *resource, |
||
68 | const gchar *path, |
||
69 | const gchar *section, |
||
70 | const gchar *prefix, |
||
71 | gboolean details) |
||
72 | { |
||
73 | gchar **children; |
||
74 | gsize size; |
||
75 | guint32 flags; |
||
76 | gint i; |
||
77 | gchar *child; |
||
78 | GError *error = NULL; |
||
79 | gint len; |
||
80 | |||
81 | children = g_resource_enumerate_children (resource, path, 0, &error); |
||
82 | if (error) |
||
83 | { |
||
84 | g_printerr ("%s\n", error->message); |
||
85 | g_error_free (error); |
||
86 | return; |
||
87 | } |
||
88 | for (i = 0; children[i]; i++) |
||
89 | { |
||
90 | child = g_strconcat (path, children[i], NULL); |
||
91 | |||
92 | len = MIN (strlen (child), strlen (prefix)); |
||
93 | if (strncmp (child, prefix, len) != 0) |
||
94 | { |
||
95 | g_free (child); |
||
96 | continue; |
||
97 | } |
||
98 | |||
99 | if (g_resource_get_info (resource, child, 0, &size, &flags, NULL)) |
||
100 | { |
||
101 | if (details) |
||
102 | g_print ("%s%s%6"G_GSIZE_FORMAT " %s %s\n", section, section[0] ? " " : "", size, flags & G_RESOURCE_FLAGS_COMPRESSED ? "c" : "u", child); |
||
103 | else |
||
104 | g_print ("%s\n", child); |
||
105 | } |
||
106 | else |
||
107 | list_resource (resource, child, section, prefix, details); |
||
108 | |||
109 | g_free (child); |
||
110 | } |
||
111 | g_strfreev (children); |
||
112 | } |
||
113 | |||
114 | static void |
||
115 | extract_resource (GResource *resource, |
||
116 | const gchar *path) |
||
117 | { |
||
118 | GBytes *bytes; |
||
119 | |||
120 | bytes = g_resource_lookup_data (resource, path, 0, NULL); |
||
121 | if (bytes != NULL) |
||
122 | { |
||
123 | gconstpointer data; |
||
124 | gsize size, written; |
||
125 | |||
126 | data = g_bytes_get_data (bytes, &size); |
||
127 | written = fwrite (data, 1, size, stdout); |
||
128 | if (written < size) |
||
129 | g_printerr ("Data truncated\n"); |
||
130 | g_bytes_unref (bytes); |
||
131 | } |
||
132 | } |
||
133 | |||
134 | /* Elf functions {{{1 */ |
||
135 | |||
136 | #ifdef HAVE_LIBELF |
||
137 | |||
138 | static Elf * |
||
139 | get_elf (const gchar *file, |
||
140 | gint *fd) |
||
141 | { |
||
142 | Elf *elf; |
||
143 | |||
144 | if (elf_version (EV_CURRENT) == EV_NONE ) |
||
145 | return NULL; |
||
146 | |||
147 | *fd = g_open (file, O_RDONLY, 0); |
||
148 | if (*fd < 0) |
||
149 | return NULL; |
||
150 | |||
151 | elf = elf_begin (*fd, ELF_C_READ, NULL); |
||
152 | if (elf == NULL) |
||
153 | { |
||
154 | g_close (*fd, NULL); |
||
155 | *fd = -1; |
||
156 | return NULL; |
||
157 | } |
||
158 | |||
159 | if (elf_kind (elf) != ELF_K_ELF) |
||
160 | { |
||
161 | g_close (*fd, NULL); |
||
162 | *fd = -1; |
||
163 | return NULL; |
||
164 | } |
||
165 | |||
166 | return elf; |
||
167 | } |
||
168 | |||
169 | typedef gboolean (*SectionCallback) (GElf_Shdr *shdr, |
||
170 | const gchar *name, |
||
171 | gpointer data); |
||
172 | |||
173 | static void |
||
174 | elf_foreach_resource_section (Elf *elf, |
||
175 | SectionCallback callback, |
||
176 | gpointer data) |
||
177 | { |
||
178 | size_t shstrndx, shnum; |
||
179 | size_t scnidx; |
||
180 | Elf_Scn *scn; |
||
181 | GElf_Shdr *shdr, shdr_mem; |
||
182 | const gchar *section_name; |
||
183 | |||
184 | elf_getshdrstrndx (elf, &shstrndx); |
||
185 | g_assert (shstrndx >= 0); |
||
186 | |||
187 | elf_getshdrnum (elf, &shnum); |
||
188 | g_assert (shnum >= 0); |
||
189 | |||
190 | for (scnidx = 1; scnidx < shnum; scnidx++) |
||
191 | { |
||
192 | scn = elf_getscn (elf, scnidx); |
||
193 | if (scn == NULL) |
||
194 | continue; |
||
195 | |||
196 | shdr = gelf_getshdr (scn, &shdr_mem); |
||
197 | if (shdr == NULL) |
||
198 | continue; |
||
199 | |||
200 | if (shdr->sh_type != SHT_PROGBITS) |
||
201 | continue; |
||
202 | |||
203 | section_name = elf_strptr (elf, shstrndx, shdr->sh_name); |
||
204 | if (section_name == NULL || |
||
205 | !g_str_has_prefix (section_name, ".gresource.")) |
||
206 | continue; |
||
207 | |||
208 | if (!callback (shdr, section_name + strlen (".gresource."), data)) |
||
209 | break; |
||
210 | } |
||
211 | } |
||
212 | |||
213 | static GResource * |
||
214 | resource_from_section (GElf_Shdr *shdr, |
||
215 | int fd) |
||
216 | { |
||
217 | gsize page_size, page_offset; |
||
218 | char *contents; |
||
219 | GResource *resource; |
||
220 | |||
221 | resource = NULL; |
||
222 | |||
223 | page_size = sysconf(_SC_PAGE_SIZE); |
||
224 | page_offset = shdr->sh_offset % page_size; |
||
225 | contents = mmap (NULL, shdr->sh_size + page_offset, |
||
226 | PROT_READ, MAP_PRIVATE, fd, shdr->sh_offset - page_offset); |
||
227 | if (contents != MAP_FAILED) |
||
228 | { |
||
229 | GBytes *bytes; |
||
230 | GError *error = NULL; |
||
231 | |||
232 | bytes = g_bytes_new_static (contents + page_offset, shdr->sh_size); |
||
233 | resource = g_resource_new_from_data (bytes, &error); |
||
234 | g_bytes_unref (bytes); |
||
235 | if (error) |
||
236 | { |
||
237 | g_printerr ("%s\n", error->message); |
||
238 | g_error_free (error); |
||
239 | } |
||
240 | } |
||
241 | else |
||
242 | { |
||
243 | g_printerr ("Can't mmap resource section"); |
||
244 | } |
||
245 | |||
246 | return resource; |
||
247 | } |
||
248 | |||
249 | typedef struct |
||
250 | { |
||
251 | int fd; |
||
252 | const gchar *section; |
||
253 | const gchar *path; |
||
254 | gboolean details; |
||
255 | gboolean found; |
||
256 | } CallbackData; |
||
257 | |||
258 | static gboolean |
||
259 | list_resources_cb (GElf_Shdr *shdr, |
||
260 | const gchar *section, |
||
261 | gpointer data) |
||
262 | { |
||
263 | CallbackData *d = data; |
||
264 | GResource *resource; |
||
265 | |||
266 | if (d->section && strcmp (section, d->section) != 0) |
||
267 | return TRUE; |
||
268 | |||
269 | d->found = TRUE; |
||
270 | |||
271 | resource = resource_from_section (shdr, d->fd); |
||
272 | list_resource (resource, "/", |
||
273 | d->section ? "" : section, |
||
274 | d->path, |
||
275 | d->details); |
||
276 | g_resource_unref (resource); |
||
277 | |||
278 | if (d->section) |
||
279 | return FALSE; |
||
280 | |||
281 | return TRUE; |
||
282 | } |
||
283 | |||
284 | static void |
||
285 | elf_list_resources (Elf *elf, |
||
286 | int fd, |
||
287 | const gchar *section, |
||
288 | const gchar *path, |
||
289 | gboolean details) |
||
290 | { |
||
291 | CallbackData data; |
||
292 | |||
293 | data.fd = fd; |
||
294 | data.section = section; |
||
295 | data.path = path; |
||
296 | data.details = details; |
||
297 | data.found = FALSE; |
||
298 | |||
299 | elf_foreach_resource_section (elf, list_resources_cb, &data); |
||
300 | |||
301 | if (!data.found) |
||
302 | g_printerr ("Can't find resource section %s\n", section); |
||
303 | } |
||
304 | |||
305 | static gboolean |
||
306 | extract_resource_cb (GElf_Shdr *shdr, |
||
307 | const gchar *section, |
||
308 | gpointer data) |
||
309 | { |
||
310 | CallbackData *d = data; |
||
311 | GResource *resource; |
||
312 | |||
313 | if (d->section && strcmp (section, d->section) != 0) |
||
314 | return TRUE; |
||
315 | |||
316 | d->found = TRUE; |
||
317 | |||
318 | resource = resource_from_section (shdr, d->fd); |
||
319 | extract_resource (resource, d->path); |
||
320 | g_resource_unref (resource); |
||
321 | |||
322 | if (d->section) |
||
323 | return FALSE; |
||
324 | |||
325 | return TRUE; |
||
326 | } |
||
327 | |||
328 | static void |
||
329 | elf_extract_resource (Elf *elf, |
||
330 | int fd, |
||
331 | const gchar *section, |
||
332 | const gchar *path) |
||
333 | { |
||
334 | CallbackData data; |
||
335 | |||
336 | data.fd = fd; |
||
337 | data.section = section; |
||
338 | data.path = path; |
||
339 | data.found = FALSE; |
||
340 | |||
341 | elf_foreach_resource_section (elf, extract_resource_cb, &data); |
||
342 | |||
343 | if (!data.found) |
||
344 | g_printerr ("Can't find resource section %s\n", section); |
||
345 | } |
||
346 | |||
347 | static gboolean |
||
348 | print_section_name (GElf_Shdr *shdr, |
||
349 | const gchar *name, |
||
350 | gpointer data) |
||
351 | { |
||
352 | g_print ("%s\n", name); |
||
353 | return TRUE; |
||
354 | } |
||
355 | |||
356 | #endif /* HAVE_LIBELF */ |
||
357 | |||
358 | /* Toplevel commands {{{1 */ |
||
359 | |||
360 | static void |
||
361 | cmd_sections (const gchar *file, |
||
362 | const gchar *section, |
||
363 | const gchar *path, |
||
364 | gboolean details) |
||
365 | { |
||
366 | GResource *resource; |
||
367 | |||
368 | #ifdef HAVE_LIBELF |
||
369 | |||
370 | Elf *elf; |
||
371 | gint fd; |
||
372 | |||
373 | if ((elf = get_elf (file, &fd))) |
||
374 | { |
||
375 | elf_foreach_resource_section (elf, print_section_name, NULL); |
||
376 | elf_end (elf); |
||
377 | close (fd); |
||
378 | } |
||
379 | else |
||
380 | |||
381 | #endif |
||
382 | |||
383 | if ((resource = get_resource (file))) |
||
384 | { |
||
385 | /* No sections */ |
||
386 | g_resource_unref (resource); |
||
387 | } |
||
388 | else |
||
389 | { |
||
390 | g_printerr ("Don't know how to handle %s\n", file); |
||
391 | #ifndef HAVE_LIBELF |
||
392 | g_printerr ("gresource is built without elf support\n"); |
||
393 | #endif |
||
394 | } |
||
395 | } |
||
396 | |||
397 | static void |
||
398 | cmd_list (const gchar *file, |
||
399 | const gchar *section, |
||
400 | const gchar *path, |
||
401 | gboolean details) |
||
402 | { |
||
403 | GResource *resource; |
||
404 | |||
405 | #ifdef HAVE_LIBELF |
||
406 | Elf *elf; |
||
407 | int fd; |
||
408 | |||
409 | if ((elf = get_elf (file, &fd))) |
||
410 | { |
||
411 | elf_list_resources (elf, fd, section, path ? path : "", details); |
||
412 | elf_end (elf); |
||
413 | close (fd); |
||
414 | } |
||
415 | else |
||
416 | |||
417 | #endif |
||
418 | |||
419 | if ((resource = get_resource (file))) |
||
420 | { |
||
421 | list_resource (resource, "/", "", path ? path : "", details); |
||
422 | g_resource_unref (resource); |
||
423 | } |
||
424 | else |
||
425 | { |
||
426 | g_printerr ("Don't know how to handle %s\n", file); |
||
427 | #ifndef HAVE_LIBELF |
||
428 | g_printerr ("gresource is built without elf support\n"); |
||
429 | #endif |
||
430 | } |
||
431 | } |
||
432 | |||
433 | static void |
||
434 | cmd_extract (const gchar *file, |
||
435 | const gchar *section, |
||
436 | const gchar *path, |
||
437 | gboolean details) |
||
438 | { |
||
439 | GResource *resource; |
||
440 | |||
441 | #ifdef HAVE_LIBELF |
||
442 | |||
443 | Elf *elf; |
||
444 | int fd; |
||
445 | |||
446 | if ((elf = get_elf (file, &fd))) |
||
447 | { |
||
448 | elf_extract_resource (elf, fd, section, path); |
||
449 | elf_end (elf); |
||
450 | close (fd); |
||
451 | } |
||
452 | else |
||
453 | |||
454 | #endif |
||
455 | |||
456 | if ((resource = get_resource (file))) |
||
457 | { |
||
458 | extract_resource (resource, path); |
||
459 | g_resource_unref (resource); |
||
460 | } |
||
461 | else |
||
462 | { |
||
463 | g_printerr ("Don't know how to handle %s\n", file); |
||
464 | #ifndef HAVE_LIBELF |
||
465 | g_printerr ("gresource is built without elf support\n"); |
||
466 | #endif |
||
467 | } |
||
468 | } |
||
469 | |||
470 | static gint |
||
471 | cmd_help (gboolean requested, |
||
472 | const gchar *command) |
||
473 | { |
||
474 | const gchar *description; |
||
475 | const gchar *synopsis; |
||
476 | gchar *option; |
||
477 | GString *string; |
||
478 | |||
479 | option = NULL; |
||
480 | |||
481 | string = g_string_new (NULL); |
||
482 | |||
483 | if (command == NULL) |
||
484 | ; |
||
485 | |||
486 | else if (strcmp (command, "help") == 0) |
||
487 | { |
||
488 | description = _("Print help"); |
||
489 | synopsis = _("[COMMAND]"); |
||
490 | } |
||
491 | |||
492 | else if (strcmp (command, "sections") == 0) |
||
493 | { |
||
494 | description = _("List sections containing resources in an elf FILE"); |
||
495 | synopsis = _("FILE"); |
||
496 | } |
||
497 | |||
498 | else if (strcmp (command, "list") == 0) |
||
499 | { |
||
500 | description = _("List resources\n" |
||
501 | "If SECTION is given, only list resources in this section\n" |
||
502 | "If PATH is given, only list matching resources"); |
||
503 | synopsis = _("FILE [PATH]"); |
||
504 | option = g_strdup_printf ("[--section %s]", _("SECTION")); |
||
505 | } |
||
506 | |||
507 | else if (strcmp (command, "details") == 0) |
||
508 | { |
||
509 | description = _("List resources with details\n" |
||
510 | "If SECTION is given, only list resources in this section\n" |
||
511 | "If PATH is given, only list matching resources\n" |
||
512 | "Details include the section, size and compression"); |
||
513 | synopsis = _("FILE [PATH]"); |
||
514 | option = g_strdup_printf ("[--section %s]", _("SECTION")); |
||
515 | } |
||
516 | |||
517 | else if (strcmp (command, "extract") == 0) |
||
518 | { |
||
519 | description = _("Extract a resource file to stdout"); |
||
520 | synopsis = _("FILE PATH"); |
||
521 | option = g_strdup_printf ("[--section %s]", _("SECTION")); |
||
522 | } |
||
523 | |||
524 | else |
||
525 | { |
||
526 | g_string_printf (string, _("Unknown command %s\n\n"), command); |
||
527 | requested = FALSE; |
||
528 | command = NULL; |
||
529 | } |
||
530 | |||
531 | if (command == NULL) |
||
532 | { |
||
533 | g_string_append (string, |
||
534 | _("Usage:\n" |
||
535 | " gresource [--section SECTION] COMMAND [ARGS...]\n" |
||
536 | "\n" |
||
537 | "Commands:\n" |
||
538 | " help Show this information\n" |
||
539 | " sections List resource sections\n" |
||
540 | " list List resources\n" |
||
541 | " details List resources with details\n" |
||
542 | " extract Extract a resource\n" |
||
543 | "\n" |
||
544 | "Use 'gresource help COMMAND' to get detailed help.\n\n")); |
||
545 | } |
||
546 | else |
||
547 | { |
||
548 | g_string_append_printf (string, _("Usage:\n gresource %s%s%s %s\n\n%s\n\n"), |
||
549 | option ? option : "", option ? " " : "", command, synopsis[0] ? synopsis : "", description); |
||
550 | |||
551 | g_string_append (string, _("Arguments:\n")); |
||
552 | |||
553 | if (option) |
||
554 | g_string_append (string, |
||
555 | _(" SECTION An (optional) elf section name\n")); |
||
556 | |||
557 | if (strstr (synopsis, _("[COMMAND]"))) |
||
558 | g_string_append (string, |
||
559 | _(" COMMAND The (optional) command to explain\n")); |
||
560 | |||
561 | if (strstr (synopsis, _("FILE"))) |
||
562 | { |
||
563 | if (strcmp (command, "sections") == 0) |
||
564 | g_string_append (string, |
||
565 | _(" FILE An elf file (a binary or a shared library)\n")); |
||
566 | else |
||
567 | g_string_append (string, |
||
568 | _(" FILE An elf file (a binary or a shared library)\n" |
||
569 | " or a compiled resource file\n")); |
||
570 | } |
||
571 | |||
572 | if (strstr (synopsis, _("[PATH]"))) |
||
573 | g_string_append (string, |
||
574 | _(" PATH An (optional) resource path (may be partial)\n")); |
||
575 | else if (strstr (synopsis, _("PATH"))) |
||
576 | g_string_append (string, |
||
577 | _(" PATH A resource path\n")); |
||
578 | |||
579 | g_string_append (string, "\n"); |
||
580 | } |
||
581 | |||
582 | if (requested) |
||
583 | g_print ("%s", string->str); |
||
584 | else |
||
585 | g_printerr ("%s\n", string->str); |
||
586 | |||
587 | g_free (option); |
||
588 | g_string_free (string, TRUE); |
||
589 | |||
590 | return requested ? 0 : 1; |
||
591 | } |
||
592 | |||
593 | /* main {{{1 */ |
||
594 | |||
595 | int |
||
596 | main (int argc, char *argv[]) |
||
597 | { |
||
598 | gchar *section = NULL; |
||
599 | gboolean details = FALSE; |
||
600 | void (* function) (const gchar *, const gchar *, const gchar *, gboolean); |
||
601 | |||
602 | #ifdef G_OS_WIN32 |
||
603 | gchar *tmp; |
||
604 | #endif |
||
605 | |||
606 | setlocale (LC_ALL, ""); |
||
607 | textdomain (GETTEXT_PACKAGE); |
||
608 | |||
609 | #ifdef G_OS_WIN32 |
||
610 | tmp = _glib_get_locale_dir (); |
||
611 | bindtextdomain (GETTEXT_PACKAGE, tmp); |
||
612 | g_free (tmp); |
||
613 | #else |
||
614 | bindtextdomain (GETTEXT_PACKAGE, GLIB_LOCALE_DIR); |
||
615 | #endif |
||
616 | |||
617 | #ifdef HAVE_BIND_TEXTDOMAIN_CODESET |
||
618 | bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
||
619 | #endif |
||
620 | |||
621 | if (argc < 2) |
||
622 | return cmd_help (FALSE, NULL); |
||
623 | |||
624 | if (argc > 3 && strcmp (argv[1], "--section") == 0) |
||
625 | { |
||
626 | section = argv[2]; |
||
627 | argv = argv + 2; |
||
628 | argc -= 2; |
||
629 | } |
||
630 | |||
631 | if (strcmp (argv[1], "help") == 0) |
||
632 | return cmd_help (TRUE, argv[2]); |
||
633 | |||
634 | else if (argc == 4 && strcmp (argv[1], "extract") == 0) |
||
635 | function = cmd_extract; |
||
636 | |||
637 | else if (argc == 3 && strcmp (argv[1], "sections") == 0) |
||
638 | function = cmd_sections; |
||
639 | |||
640 | else if ((argc == 3 || argc == 4) && strcmp (argv[1], "list") == 0) |
||
641 | { |
||
642 | function = cmd_list; |
||
643 | details = FALSE; |
||
644 | } |
||
645 | else if ((argc == 3 || argc == 4) && strcmp (argv[1], "details") == 0) |
||
646 | { |
||
647 | function = cmd_list; |
||
648 | details = TRUE; |
||
649 | } |
||
650 | else |
||
651 | return cmd_help (FALSE, argv[1]); |
||
652 | |||
653 | (* function) (argv[2], section, argc > 3 ? argv[3] : NULL, details); |
||
654 | |||
655 | return 0; |
||
656 | } |
||
657 | |||
658 | /* vim:set foldmethod=marker: */ |