1 | /* |
2 | * Copyright 2015 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.1 of the License, 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 <mclasen@redhat.com> |
18 | */ |
19 | |
20 | #include "config.h" |
21 | |
22 | #include <gio/gio.h> |
23 | #include <gi18n.h> |
24 | #include <stdlib.h> |
25 | |
26 | #include "gio-tool.h" |
27 | |
28 | |
29 | static char *attr_type = "string" ; |
30 | static gboolean nofollow_symlinks = FALSE; |
31 | |
32 | static const GOptionEntry entries[] = { |
33 | { "type" , 't', 0, G_OPTION_ARG_STRING, &attr_type, N_("Type of the attribute" ), N_("TYPE" ) }, |
34 | { "nofollow-symlinks" , 'n', 0, G_OPTION_ARG_NONE, &nofollow_symlinks, N_("Don’t follow symbolic links" ), NULL }, |
35 | { NULL } |
36 | }; |
37 | |
38 | static char * |
39 | hex_unescape (const char *str) |
40 | { |
41 | int i; |
42 | char *unescaped_str, *p; |
43 | unsigned char c; |
44 | int len; |
45 | |
46 | len = strlen (s: str); |
47 | unescaped_str = g_malloc (n_bytes: len + 1); |
48 | |
49 | p = unescaped_str; |
50 | for (i = 0; i < len; i++) |
51 | { |
52 | if (str[i] == '\\' && |
53 | str[i+1] == 'x' && |
54 | len - i >= 4) |
55 | { |
56 | c = |
57 | (g_ascii_xdigit_value (c: str[i+2]) << 4) | |
58 | g_ascii_xdigit_value (c: str[i+3]); |
59 | *p++ = c; |
60 | i += 3; |
61 | } |
62 | else |
63 | *p++ = str[i]; |
64 | } |
65 | *p++ = 0; |
66 | |
67 | return unescaped_str; |
68 | } |
69 | |
70 | int |
71 | handle_set (int argc, char *argv[], gboolean do_help) |
72 | { |
73 | GOptionContext *context; |
74 | GError *error = NULL; |
75 | GFile *file; |
76 | const char *attribute; |
77 | GFileAttributeType type; |
78 | gpointer value; |
79 | gboolean b; |
80 | guint32 uint32; |
81 | gint32 int32; |
82 | guint64 uint64; |
83 | gint64 int64; |
84 | gchar *param; |
85 | |
86 | g_set_prgname (prgname: "gio set" ); |
87 | |
88 | /* Translators: commandline placeholder */ |
89 | param = g_strdup_printf (format: "%s %s %s…" , _("LOCATION" ), _("ATTRIBUTE" ), _("VALUE" )); |
90 | context = g_option_context_new (parameter_string: param); |
91 | g_free (mem: param); |
92 | g_option_context_set_help_enabled (context, FALSE); |
93 | g_option_context_set_summary (context, _("Set a file attribute of LOCATION." )); |
94 | g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE); |
95 | |
96 | if (do_help) |
97 | { |
98 | show_help (context, NULL); |
99 | g_option_context_free (context); |
100 | return 0; |
101 | } |
102 | |
103 | if (!g_option_context_parse (context, argc: &argc, argv: &argv, error: &error)) |
104 | { |
105 | show_help (context, message: error->message); |
106 | g_error_free (error); |
107 | g_option_context_free (context); |
108 | return 1; |
109 | } |
110 | |
111 | if (argc < 2) |
112 | { |
113 | show_help (context, _("Location not specified" )); |
114 | g_option_context_free (context); |
115 | return 1; |
116 | } |
117 | |
118 | if (argc < 3) |
119 | { |
120 | show_help (context, _("Attribute not specified" )); |
121 | g_option_context_free (context); |
122 | return 1; |
123 | } |
124 | |
125 | attribute = argv[2]; |
126 | |
127 | type = attribute_type_from_string (str: attr_type); |
128 | if ((argc < 4) && (type != G_FILE_ATTRIBUTE_TYPE_INVALID)) |
129 | { |
130 | show_help (context, _("Value not specified" )); |
131 | g_option_context_free (context); |
132 | return 1; |
133 | } |
134 | |
135 | if ((argc > 4) && (type != G_FILE_ATTRIBUTE_TYPE_STRINGV)) |
136 | { |
137 | show_help (context, _("Too many arguments" )); |
138 | g_option_context_free (context); |
139 | return 1; |
140 | } |
141 | |
142 | g_option_context_free (context); |
143 | |
144 | switch (type) |
145 | { |
146 | case G_FILE_ATTRIBUTE_TYPE_STRING: |
147 | value = argv[3]; |
148 | break; |
149 | case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING: |
150 | value = hex_unescape (str: argv[3]); |
151 | break; |
152 | case G_FILE_ATTRIBUTE_TYPE_BOOLEAN: |
153 | b = g_ascii_strcasecmp (s1: argv[3], s2: "true" ) == 0; |
154 | value = &b; |
155 | break; |
156 | case G_FILE_ATTRIBUTE_TYPE_UINT32: |
157 | uint32 = atol (nptr: argv[3]); |
158 | value = &uint32; |
159 | break; |
160 | case G_FILE_ATTRIBUTE_TYPE_INT32: |
161 | int32 = atol (nptr: argv[3]); |
162 | value = &int32; |
163 | break; |
164 | case G_FILE_ATTRIBUTE_TYPE_UINT64: |
165 | uint64 = g_ascii_strtoull (nptr: argv[3], NULL, base: 10); |
166 | value = &uint64; |
167 | break; |
168 | case G_FILE_ATTRIBUTE_TYPE_INT64: |
169 | int64 = g_ascii_strtoll (nptr: argv[3], NULL, base: 10); |
170 | value = &int64; |
171 | break; |
172 | case G_FILE_ATTRIBUTE_TYPE_STRINGV: |
173 | value = &argv[3]; |
174 | break; |
175 | case G_FILE_ATTRIBUTE_TYPE_INVALID: |
176 | value = NULL; |
177 | break; |
178 | case G_FILE_ATTRIBUTE_TYPE_OBJECT: |
179 | default: |
180 | print_error (_("Invalid attribute type “%s”" ), attr_type); |
181 | return 1; |
182 | } |
183 | |
184 | file = g_file_new_for_commandline_arg (arg: argv[1]); |
185 | |
186 | if (!g_file_set_attribute (file, |
187 | attribute, |
188 | type, |
189 | value_p: value, |
190 | flags: nofollow_symlinks ? |
191 | G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS : |
192 | G_FILE_QUERY_INFO_NONE, |
193 | NULL, error: &error)) |
194 | { |
195 | print_error (format: "%s" , error->message); |
196 | g_error_free (error); |
197 | g_object_unref (object: file); |
198 | return 1; |
199 | } |
200 | |
201 | g_object_unref (object: file); |
202 | |
203 | return 0; |
204 | } |
205 | |