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
26static struct option const kLongOptions[] = {
27	{"help", no_argument, 0, 'h'},
28	{NULL}
29};
30
31
32extern const char *__progname;
33static const char *kProgramName = __progname;
34
35
36// supported types (if you add any, make sure that writeAttr() handles
37// them properly)
38
39const 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};
66const 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*/
77static status_t
78typeForString(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
105void
106usage(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
124void
125invalidAttrType(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
138void
139invalidTypeCode(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
149void
150invalidBoolValue(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
161int
162main(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