xref: /haiku/src/bin/addattr/main.cpp (revision d113c00b5ac2cc233ff95bc887d4a32194c91e62)
1 /*
2  * Copyright 2010, Jérôme Duval.
3  * Copyright 2004-2015, Axel Dörfler, axeld@pinc-software.de.
4  * Copyright 2002, Sebastian Nozzi.
5  *
6  * Distributed under the terms of the MIT license.
7  */
8 
9 
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include <File.h>
16 #include <Mime.h>
17 #include <TypeConstants.h>
18 
19 #include "addAttr.h"
20 
21 
22 #define ERR(msg, args...)	fprintf(stderr, "%s: " msg, kProgramName, args)
23 #define ERR_0(msg)			fprintf(stderr, "%s: " msg, kProgramName)
24 
25 
26 static struct option const kLongOptions[] = {
27 	{"help", no_argument, 0, 'h'},
28 	{NULL}
29 };
30 
31 
32 extern const char *__progname;
33 static const char *kProgramName = __progname;
34 
35 
36 // supported types (if you add any, make sure that writeAttr() handles
37 // them properly)
38 
39 const struct {
40 	type_code	type;
41 	const char	*name;
42 } kSupportedTypes[] = {
43 	{B_STRING_TYPE, "string"},
44 	{B_MIME_STRING_TYPE, "mime"},
45 
46 	{B_INT32_TYPE, "int32"},
47 	{B_INT32_TYPE, "int"},
48 	{B_UINT32_TYPE, "uint32"},
49 	{B_UINT32_TYPE, "uint"},
50 
51 	{B_INT64_TYPE, "int64"},
52 	{B_INT64_TYPE, "llong"},
53 	{B_UINT64_TYPE, "uint64"},
54 	{B_UINT64_TYPE, "ullong"},
55 
56 	{B_FLOAT_TYPE, "float"},
57 	{B_DOUBLE_TYPE, "double"},
58 
59 	{B_BOOL_TYPE, "bool"},
60 
61 	{B_TIME_TYPE, "time"},
62 
63 	{B_VECTOR_ICON_TYPE, "icon"},
64 	{B_RAW_TYPE, "raw"},
65 };
66 const uint32 kNumSupportedTypes = sizeof(kSupportedTypes)
67 	/ sizeof(kSupportedTypes[0]);
68 
69 
70 /*!	For the given string that the user specifies as attribute type
71 	in the command line, this function tries to figure out the
72 	corresponding Be API value.
73 
74 	On success, "result" will contain that value
75 	On failure, B_BAD_VALUE is returned and "result" is not modified
76 */
77 static status_t
typeForString(const char * string,type_code * _result)78 typeForString(const char* string, type_code* _result)
79 {
80 	for (uint32 i = 0; i < kNumSupportedTypes; i++) {
81 		if (!strcmp(string, kSupportedTypes[i].name)) {
82 			*_result = kSupportedTypes[i].type;
83 			return B_OK;
84 		}
85 	}
86 
87 	// type didn't show up - in this case, we try to parse
88 	// the string as number and use it directly as type code
89 
90 	if (sscanf(string, "%" B_SCNi32, _result) == 1)
91 		return B_OK;
92 
93 	// if that didn't work, try the string as a char-type-code
94 	// enclosed in single quotes
95 	uchar type[4];
96 	if (sscanf(string, "'%c%c%c%c'", &type[0], &type[1], &type[2], &type[3]) == 4) {
97 		*_result = (type[0] << 24) | (type[1] << 16) | (type[2] << 8) | type[3];
98 		return B_OK;
99 	}
100 
101 	return B_BAD_VALUE;
102 }
103 
104 
105 void
usage(int returnValue)106 usage(int returnValue)
107 {
108 	fprintf(stderr, "usage: %s [-t type|-c code] [ -P ] attr value file1 [file2...]\n"
109 		"   or: %s [-f value-from-file] [-t type|-c code] [ -P ] attr file1 [file2...]\n\n"
110 		"\t-P : Don't resolve links\n"
111 		"\tThe '-t' and '-c' options are alternatives; use one or the other.\n"
112 		"\ttype is one of:\n"
113 		"\t\tstring, mime, int, int32, uint32, llong, int64, uint64,\n"
114 		"\t\tfloat, double, bool, icon, time, raw\n"
115 		"\t\tor a numeric value (ie. 0x1234, 42, ...),\n"
116 		"\t\tor an escape-quoted type code, eg. \\'MICN\\'\n"
117 		"\tThe default is \"string\"\n"
118 		"\tcode is a four-char type ID (eg. MICN)\n", kProgramName, kProgramName);
119 
120 	exit(returnValue);
121 }
122 
123 
124 void
invalidAttrType(const char * attrTypeName)125 invalidAttrType(const char* attrTypeName)
126 {
127 	fprintf(stderr, "%s: attribute type \"%s\" is not valid\n", kProgramName,
128 		attrTypeName);
129 	fprintf(stderr, "\tTry one of: string, mime, int, llong, float, double,\n"
130 		"\t\tbool, icon, time, raw, or a numeric value (ie. 0x1234, 42, ...),\n"
131 		"\t\tor a quoted type code, eg.: \\'MICN\\'\n"
132 		"\t\tOr enter the actual type code with the '-c' option\n");
133 
134 	exit(1);
135 }
136 
137 
138 void
invalidTypeCode(const char * attrTypeName)139 invalidTypeCode(const char* attrTypeName)
140 {
141 	fprintf(stderr, "%s: attribute type code \"%s\" is not valid\n", kProgramName,
142 		attrTypeName);
143 	fprintf(stderr, "\tIt must be exactly four characters\n");
144 
145 	exit(1);
146 }
147 
148 
149 void
invalidBoolValue(const char * value)150 invalidBoolValue(const char* value)
151 {
152 	fprintf(stderr, "%s: attribute value \"%s\" is not valid\n", kProgramName,
153 		value);
154 	fprintf(stderr, "\tBool accepts: 0, f, false, disabled, off,\n"
155 		"\t\t1, t, true, enabled, on\n");
156 
157 	exit(1);
158 }
159 
160 
161 int
main(int argc,char * argv[])162 main(int argc, char* argv[])
163 {
164 	type_code attrType = B_STRING_TYPE;
165 	char* attrValue = NULL;
166 	size_t valueFileLength = 0;
167 	bool resolveLinks = true;
168 
169 	int c;
170 	while ((c = getopt_long(argc, argv, "hf:t:c:P", kLongOptions, NULL)) != -1) {
171 		switch (c) {
172 			case 0:
173 				break;
174 			case 'f':
175 			{
176 				// retrieve attribute value from file
177 				BFile file;
178 				off_t size;
179 				status_t status = file.SetTo(optarg, B_READ_ONLY);
180 				if (status < B_OK) {
181 					ERR("can't read attribute value from file %s: %s\n",
182 						optarg, strerror(status));
183 					return 1;
184 				}
185 
186 				status = file.GetSize(&size);
187 				if (status == B_OK) {
188 					if (size == 0) {
189 						ERR_0("attribute value is empty: 0 bytes\n");
190 						return 1;
191 					}
192 					if (size > 4 * 1024 * 1024) {
193 						ERR("attribute value is too large: %" B_PRIdOFF
194 							" bytes\n", size);
195 						return 1;
196 					}
197 					attrValue = (char*)malloc(size);
198 					if (attrValue != NULL)
199 						status = file.Read(attrValue, size);
200 					else
201 						status = B_NO_MEMORY;
202 				}
203 
204 				if (status < B_OK) {
205 					ERR("can't read attribute value: %s\n", strerror(status));
206 					return 1;
207 				}
208 
209 				valueFileLength = (size_t)size;
210 				break;
211 			}
212 			case 't':
213 				// Get the attribute type
214 				if (typeForString(optarg, &attrType) != B_OK)
215 					invalidAttrType(optarg);
216 				break;
217 			case 'c':
218 				if (strlen(optarg) == 4) {
219 					// Get the type code directly
220 					char code[] = "'    '";
221 					strncpy(code + 1, optarg, 4);
222 					if (typeForString(code, &attrType) == B_OK)
223 						break;
224 				}
225 				invalidTypeCode(optarg);
226 			case 'P':
227 				resolveLinks = false;
228 				break;
229 			case 'h':
230 				usage(0);
231 				break;
232 			default:
233 				usage(1);
234 				break;
235 		}
236 	}
237 
238 	if (argc - optind < 1)
239 		usage(1);
240 	const char* attrName = argv[optind++];
241 
242 	if (argc - optind < 1)
243 		usage(1);
244 	if (!valueFileLength)
245 		attrValue = argv[optind++];
246 
247 	if (argc - optind < 1)
248 		usage(1);
249 
250 	// Now that we gathered all the information proceed
251 	// to add the attribute to the file(s)
252 
253 	int result = 0;
254 
255 	for (; optind < argc; optind++) {
256 		status_t status = addAttr(argv[optind], attrType, attrName, attrValue,
257 			valueFileLength, resolveLinks);
258 
259 		// special case for bool types
260 		if (status == B_BAD_VALUE && attrType == B_BOOL_TYPE)
261 			invalidBoolValue(attrValue);
262 
263 		if (status != B_OK) {
264 			ERR("can't add attribute to file %s: %s\n", argv[optind],
265 				strerror(status));
266 
267 			// proceed files, but return an error at the end
268 			result = 1;
269 		}
270 	}
271 
272 	if (valueFileLength)
273 		free(attrValue);
274 
275 	return result;
276 }
277 
278