12ae45f0cSAxel Dörfler/*
2b54538ccSJérôme Duval * Copyright 2010, J��r��me Duval.
306732c7cSAxel Dörfler * Copyright 2004-2015, Axel D��rfler, axeld@pinc-software.de.
42ae45f0cSAxel Dörfler * Copyright 2002, Sebastian Nozzi.
52ae45f0cSAxel Dörfler *
62ae45f0cSAxel Dörfler * Distributed under the terms of the MIT license.
72ae45f0cSAxel Dörfler */
8bb103dcaSFrançois Revol
9bb103dcaSFrançois Revol
10b54538ccSJérôme Duval#include <getopt.h>
11b54538ccSJérôme Duval#include <stdio.h>
12b54538ccSJérôme Duval#include <stdlib.h>
13b54538ccSJérôme Duval#include <string.h>
1419ab5531SAxel Dörfler
15fcdd9a92SAxel Dörfler#include <File.h>
1619ab5531SAxel Dörfler#include <Mime.h>
17fcdd9a92SAxel Dörfler#include <TypeConstants.h>
18bb103dcaSFrançois Revol
19b54538ccSJérôme Duval#include "addAttr.h"
20b54538ccSJérôme Duval
21b54538ccSJérôme Duval
22b54538ccSJérôme Duval#define ERR(msg, args...)	fprintf(stderr, "%s: " msg, kProgramName, args)
23b54538ccSJérôme Duval#define ERR_0(msg)			fprintf(stderr, "%s: " msg, kProgramName)
24b54538ccSJérôme Duval
25b54538ccSJérôme Duval
26b54538ccSJérôme Duvalstatic struct option const kLongOptions[] = {
27b54538ccSJérôme Duval	{"help", no_argument, 0, 'h'},
28b54538ccSJérôme Duval	{NULL}
29b54538ccSJérôme Duval};
30b54538ccSJérôme Duval
31b54538ccSJérôme Duval
32b54538ccSJérôme Duvalextern const char *__progname;
33b54538ccSJérôme Duvalstatic const char *kProgramName = __progname;
34bb103dcaSFrançois Revol
35bb103dcaSFrançois Revol
36684f9b91SAxel Dörfler// supported types (if you add any, make sure that writeAttr() handles
37684f9b91SAxel Dörfler// them properly)
382ae45f0cSAxel Dörfler
392ae45f0cSAxel Dörflerconst struct {
402ae45f0cSAxel Dörfler	type_code	type;
412ae45f0cSAxel Dörfler	const char	*name;
422ae45f0cSAxel Dörfler} kSupportedTypes[] = {
432ae45f0cSAxel Dörfler	{B_STRING_TYPE, "string"},
442ae45f0cSAxel Dörfler	{B_MIME_STRING_TYPE, "mime"},
452ae45f0cSAxel Dörfler
462ae45f0cSAxel Dörfler	{B_INT32_TYPE, "int32"},
472ae45f0cSAxel Dörfler	{B_INT32_TYPE, "int"},
482ae45f0cSAxel Dörfler	{B_UINT32_TYPE, "uint32"},
492ae45f0cSAxel Dörfler	{B_UINT32_TYPE, "uint"},
502ae45f0cSAxel Dörfler
512ae45f0cSAxel Dörfler	{B_INT64_TYPE, "int64"},
522ae45f0cSAxel Dörfler	{B_INT64_TYPE, "llong"},
532ae45f0cSAxel Dörfler	{B_UINT64_TYPE, "uint64"},
542ae45f0cSAxel Dörfler	{B_UINT64_TYPE, "ullong"},
552ae45f0cSAxel Dörfler
562ae45f0cSAxel Dörfler	{B_FLOAT_TYPE, "float"},
572ae45f0cSAxel Dörfler	{B_DOUBLE_TYPE, "double"},
582ae45f0cSAxel Dörfler
592ae45f0cSAxel Dörfler	{B_BOOL_TYPE, "bool"},
602384e640SAxel Dörfler
6178c9dabdSAxel Dörfler	{B_TIME_TYPE, "time"},
6298dc4f9cSAxel Dörfler
6378c9dabdSAxel Dörfler	{B_VECTOR_ICON_TYPE, "icon"},
642384e640SAxel Dörfler	{B_RAW_TYPE, "raw"},
652ae45f0cSAxel Dörfler};
66684f9b91SAxel Dörflerconst uint32 kNumSupportedTypes = sizeof(kSupportedTypes)
67684f9b91SAxel Dörfler	/ sizeof(kSupportedTypes[0]);
682ae45f0cSAxel Dörfler
692ae45f0cSAxel Dörfler
70684f9b91SAxel Dörfler/*!	For the given string that the user specifies as attribute type
71684f9b91SAxel Dörfler	in the command line, this function tries to figure out the
72684f9b91SAxel Dörfler	corresponding Be API value.
732ae45f0cSAxel Dörfler
74684f9b91SAxel Dörfler	On success, "result" will contain that value
75684f9b91SAxel Dörfler	On failure, B_BAD_VALUE is returned and "result" is not modified
76684f9b91SAxel Dörfler*/
772ae45f0cSAxel Dörflerstatic status_t
7806732c7cSAxel DörflertypeForString(const char* string, type_code* _result)
79bb103dcaSFrançois Revol{
802ae45f0cSAxel Dörfler	for (uint32 i = 0; i < kNumSupportedTypes; i++) {
812ae45f0cSAxel Dörfler		if (!strcmp(string, kSupportedTypes[i].name)) {
822ae45f0cSAxel Dörfler			*_result = kSupportedTypes[i].type;
832ae45f0cSAxel Dörfler			return B_OK;
84bb103dcaSFrançois Revol		}
85bb103dcaSFrançois Revol	}
86bb103dcaSFrançois Revol
877beac31fSPete Goodeve	// type didn't show up - in this case, we try to parse
887beac31fSPete Goodeve	// the string as number and use it directly as type code
892ae45f0cSAxel Dörfler
907beac31fSPete Goodeve	if (sscanf(string, "%" B_SCNi32, _result) == 1)
912ae45f0cSAxel Dörfler		return B_OK;
922ae45f0cSAxel Dörfler
937beac31fSPete Goodeve	// if that didn't work, try the string as a char-type-code
947beac31fSPete Goodeve	// enclosed in single quotes
95fcdd9a92SAxel Dörfler	uchar type[4];
96fcdd9a92SAxel Dörfler	if (sscanf(string, "'%c%c%c%c'", &type[0], &type[1], &type[2], &type[3]) == 4) {
97fcdd9a92SAxel Dörfler		*_result = (type[0] << 24) | (type[1] << 16) | (type[2] << 8) | type[3];
98fcdd9a92SAxel Dörfler		return B_OK;
99fcdd9a92SAxel Dörfler	}
100fcdd9a92SAxel Dörfler
1012ae45f0cSAxel Dörfler	return B_BAD_VALUE;
102bb103dcaSFrançois Revol}
103bb103dcaSFrançois Revol
1042ae45f0cSAxel Dörfler
1052ae45f0cSAxel Dörflervoid
106fcdd9a92SAxel Dörflerusage(int returnValue)
107bb103dcaSFrançois Revol{
1087beac31fSPete Goodeve	fprintf(stderr, "usage: %s [-t type|-c code] [ -P ] attr value file1 [file2...]\n"
1097beac31fSPete Goodeve		"   or: %s [-f value-from-file] [-t type|-c code] [ -P ] attr file1 [file2...]\n\n"
1108b20dbc8SJérôme Duval		"\t-P : Don't resolve links\n"
1117beac31fSPete Goodeve		"\tThe '-t' and '-c' options are alternatives; use one or the other.\n"
1127beac31fSPete Goodeve		"\ttype is one of:\n"
1137beac31fSPete Goodeve		"\t\tstring, mime, int, int32, uint32, llong, int64, uint64,\n"
1147beac31fSPete Goodeve		"\t\tfloat, double, bool, icon, time, raw\n"
1157beac31fSPete Goodeve		"\t\tor a numeric value (ie. 0x1234, 42, ...),\n"
1167beac31fSPete Goodeve		"\t\tor an escape-quoted type code, eg. \\'MICN\\'\n"
1177beac31fSPete Goodeve		"\tThe default is \"string\"\n"
1187beac31fSPete Goodeve		"\tcode is a four-char type ID (eg. MICN)\n", kProgramName, kProgramName);
119fcdd9a92SAxel Dörfler
120fcdd9a92SAxel Dörfler	exit(returnValue);
121fcdd9a92SAxel Dörfler}
1222ae45f0cSAxel Dörfler
123fcdd9a92SAxel Dörfler
1242ae45f0cSAxel Dörflervoid
12578c9dabdSAxel DörflerinvalidAttrType(const char* attrTypeName)
1262ae45f0cSAxel Dörfler{
127b54538ccSJérôme Duval	fprintf(stderr, "%s: attribute type \"%s\" is not valid\n", kProgramName,
128684f9b91SAxel Dörfler		attrTypeName);
1297beac31fSPete Goodeve	fprintf(stderr, "\tTry one of: string, mime, int, llong, float, double,\n"
1307beac31fSPete Goodeve		"\t\tbool, icon, time, raw, or a numeric value (ie. 0x1234, 42, ...),\n"
1317beac31fSPete Goodeve		"\t\tor a quoted type code, eg.: \\'MICN\\'\n"
1327beac31fSPete Goodeve		"\t\tOr enter the actual type code with the '-c' option\n");
1337beac31fSPete Goodeve
1347beac31fSPete Goodeve	exit(1);
1357beac31fSPete Goodeve}
1367beac31fSPete Goodeve
1377beac31fSPete Goodeve
1387beac31fSPete Goodevevoid
1397beac31fSPete GoodeveinvalidTypeCode(const char* attrTypeName)
1407beac31fSPete Goodeve{
1417beac31fSPete Goodeve	fprintf(stderr, "%s: attribute type code \"%s\" is not valid\n", kProgramName,
1427beac31fSPete Goodeve		attrTypeName);
1437beac31fSPete Goodeve	fprintf(stderr, "\tIt must be exactly four characters\n");
1442ae45f0cSAxel Dörfler
1452ae45f0cSAxel Dörfler	exit(1);
1462ae45f0cSAxel Dörfler}
1472ae45f0cSAxel Dörfler
1482ae45f0cSAxel Dörfler
1492ae45f0cSAxel Dörflervoid
15006732c7cSAxel DörflerinvalidBoolValue(const char* value)
1512ae45f0cSAxel Dörfler{
152b54538ccSJérôme Duval	fprintf(stderr, "%s: attribute value \"%s\" is not valid\n", kProgramName,
153684f9b91SAxel Dörfler		value);
1547beac31fSPete Goodeve	fprintf(stderr, "\tBool accepts: 0, f, false, disabled, off,\n"
1557beac31fSPete Goodeve		"\t\t1, t, true, enabled, on\n");
1562ae45f0cSAxel Dörfler
1572ae45f0cSAxel Dörfler	exit(1);
1582ae45f0cSAxel Dörfler}
1592ae45f0cSAxel Dörfler
1602ae45f0cSAxel Dörfler
1612ae45f0cSAxel Dörflerint
16206732c7cSAxel Dörflermain(int argc, char* argv[])
1632ae45f0cSAxel Dörfler{
1642ae45f0cSAxel Dörfler	type_code attrType = B_STRING_TYPE;
16506732c7cSAxel Dörfler	char* attrValue = NULL;
166fcdd9a92SAxel Dörfler	size_t valueFileLength = 0;
1678b20dbc8SJérôme Duval	bool resolveLinks = true;
1682ae45f0cSAxel Dörfler
169b54538ccSJérôme Duval	int c;
1707beac31fSPete Goodeve	while ((c = getopt_long(argc, argv, "hf:t:c:P", kLongOptions, NULL)) != -1) {
171b54538ccSJérôme Duval		switch (c) {
172b54538ccSJérôme Duval			case 0:
173b54538ccSJérôme Duval				break;
174b54538ccSJérôme Duval			case 'f':
175b54538ccSJérôme Duval			{
176b54538ccSJérôme Duval				// retrieve attribute value from file
177b54538ccSJérôme Duval				BFile file;
178b54538ccSJérôme Duval				off_t size;
179b54538ccSJérôme Duval				status_t status = file.SetTo(optarg, B_READ_ONLY);
180b54538ccSJérôme Duval				if (status < B_OK) {
181b54538ccSJérôme Duval					ERR("can't read attribute value from file %s: %s\n",
182b54538ccSJérôme Duval						optarg, strerror(status));
183b54538ccSJérôme Duval					return 1;
184b54538ccSJérôme Duval				}
1857beac31fSPete Goodeve
186b54538ccSJérôme Duval				status = file.GetSize(&size);
187b54538ccSJérôme Duval				if (status == B_OK) {
188b54538ccSJérôme Duval					if (size == 0) {
189b54538ccSJérôme Duval						ERR_0("attribute value is empty: 0 bytes\n");
190b54538ccSJérôme Duval						return 1;
191b54538ccSJérôme Duval					}
192b54538ccSJérôme Duval					if (size > 4 * 1024 * 1024) {
193b54538ccSJérôme Duval						ERR("attribute value is too large: %" B_PRIdOFF
194b54538ccSJérôme Duval							" bytes\n", size);
195b54538ccSJérôme Duval						return 1;
196b54538ccSJérôme Duval					}
19706732c7cSAxel Dörfler					attrValue = (char*)malloc(size);
198b54538ccSJérôme Duval					if (attrValue != NULL)
199b54538ccSJérôme Duval						status = file.Read(attrValue, size);
200b54538ccSJérôme Duval					else
201b54538ccSJérôme Duval						status = B_NO_MEMORY;
202b54538ccSJérôme Duval				}
2037beac31fSPete Goodeve
204b54538ccSJérôme Duval				if (status < B_OK) {
205b54538ccSJérôme Duval					ERR("can't read attribute value: %s\n", strerror(status));
206b54538ccSJérôme Duval					return 1;
207b54538ccSJérôme Duval				}
2087beac31fSPete Goodeve
209b54538ccSJérôme Duval				valueFileLength = (size_t)size;
210b54538ccSJérôme Duval				break;
211684f9b91SAxel Dörfler			}
212b54538ccSJérôme Duval			case 't':
213b54538ccSJérôme Duval				// Get the attribute type
214b54538ccSJérôme Duval				if (typeForString(optarg, &attrType) != B_OK)
215b54538ccSJérôme Duval					invalidAttrType(optarg);
216b54538ccSJérôme Duval				break;
2177beac31fSPete Goodeve			case 'c':
2187beac31fSPete Goodeve				if (strlen(optarg) == 4) {
2197beac31fSPete Goodeve					// Get the type code directly
2207beac31fSPete Goodeve					char code[] = "'    '";
2217beac31fSPete Goodeve					strncpy(code + 1, optarg, 4);
2227beac31fSPete Goodeve					if (typeForString(code, &attrType) == B_OK)
2237beac31fSPete Goodeve						break;
2247beac31fSPete Goodeve				}
2257beac31fSPete Goodeve				invalidTypeCode(optarg);
226b54538ccSJérôme Duval			case 'P':
227b54538ccSJérôme Duval				resolveLinks = false;
228b54538ccSJérôme Duval				break;
229b54538ccSJérôme Duval			case 'h':
230b54538ccSJérôme Duval				usage(0);
231b54538ccSJérôme Duval				break;
232b54538ccSJérôme Duval			default:
233b54538ccSJérôme Duval				usage(1);
234b54538ccSJérôme Duval				break;
235fcdd9a92SAxel Dörfler		}
236fcdd9a92SAxel Dörfler	}
2377beac31fSPete Goodeve
238b54538ccSJérôme Duval	if (argc - optind < 1)
239b54538ccSJérôme Duval		usage(1);
24006732c7cSAxel Dörfler	const char* attrName = argv[optind++];
241fcdd9a92SAxel Dörfler
242b54538ccSJérôme Duval	if (argc - optind < 1)
243b54538ccSJérôme Duval		usage(1);
244fcdd9a92SAxel Dörfler	if (!valueFileLength)
245b54538ccSJérôme Duval		attrValue = argv[optind++];
246fcdd9a92SAxel Dörfler
247b54538ccSJérôme Duval	if (argc - optind < 1)
248b54538ccSJérôme Duval		usage(1);
2492ae45f0cSAxel Dörfler
2502ae45f0cSAxel Dörfler	// Now that we gathered all the information proceed
2512ae45f0cSAxel Dörfler	// to add the attribute to the file(s)
2522ae45f0cSAxel Dörfler
2532ae45f0cSAxel Dörfler	int result = 0;
2542ae45f0cSAxel Dörfler
255b54538ccSJérôme Duval	for (; optind < argc; optind++) {
256b54538ccSJérôme Duval		status_t status = addAttr(argv[optind], attrType, attrName, attrValue,
2578b20dbc8SJérôme Duval			valueFileLength, resolveLinks);
2582ae45f0cSAxel Dörfler
2592ae45f0cSAxel Dörfler		// special case for bool types
2602ae45f0cSAxel Dörfler		if (status == B_BAD_VALUE && attrType == B_BOOL_TYPE)
2612ae45f0cSAxel Dörfler			invalidBoolValue(attrValue);
2622ae45f0cSAxel Dörfler
2632ae45f0cSAxel Dörfler		if (status != B_OK) {
264b54538ccSJérôme Duval			ERR("can't add attribute to file %s: %s\n", argv[optind],
265b54538ccSJérôme Duval				strerror(status));
2662ae45f0cSAxel Dörfler
2672ae45f0cSAxel Dörfler			// proceed files, but return an error at the end
2682ae45f0cSAxel Dörfler			result = 1;
269bb103dcaSFrançois Revol		}
270bb103dcaSFrançois Revol	}
271bb103dcaSFrançois Revol
272fcdd9a92SAxel Dörfler	if (valueFileLength)
273fcdd9a92SAxel Dörfler		free(attrValue);
274fcdd9a92SAxel Dörfler
2752ae45f0cSAxel Dörfler	return result;
276bb103dcaSFrançois Revol}
277bb103dcaSFrançois Revol