--- a/src/utils/pxattr.cpp
+++ b/src/utils/pxattr.cpp
@@ -405,7 +405,7 @@
return true;
}
-static const string cstr_nullstring("");
+static const string nullstring("");
bool get(const string& path, const string& _name, string *value,
flags flags, nspace dom)
@@ -414,7 +414,7 @@
}
bool get(int fd, const string& _name, string *value, flags flags, nspace dom)
{
- return get(fd, cstr_nullstring, _name, value, flags, dom);
+ return get(fd, nullstring, _name, value, flags, dom);
}
bool set(const string& path, const string& _name, const string& value,
flags flags, nspace dom)
@@ -424,7 +424,7 @@
bool set(int fd, const string& _name, const string& value,
flags flags, nspace dom)
{
- return set(fd, cstr_nullstring, _name, value, flags, dom);
+ return set(fd, nullstring, _name, value, flags, dom);
}
bool del(const string& path, const string& _name, flags flags, nspace dom)
{
@@ -432,7 +432,7 @@
}
bool del(int fd, const string& _name, flags flags, nspace dom)
{
- return del(fd, cstr_nullstring, _name, flags, dom);
+ return del(fd, nullstring, _name, flags, dom);
}
bool list(const string& path, vector<string>* names, flags flags, nspace dom)
{
@@ -440,33 +440,37 @@
}
bool list(int fd, vector<string>* names, flags flags, nspace dom)
{
- return list(fd, cstr_nullstring, names, flags, dom);
-}
-
-static const string cstr_userstring("user.");
+ return list(fd, nullstring, names, flags, dom);
+}
+
+#if defined(__gnu_linux__) || defined(COMPAT1)
+static const string userstring("user.");
+#else
+static const string userstring("");
+#endif
bool sysname(nspace dom, const string& pname, string* sname)
{
if (dom != PXATTR_USER) {
errno = EINVAL;
return false;
}
- *sname = cstr_userstring + pname;
+ *sname = userstring + pname;
return true;
}
bool pxname(nspace dom, const string& sname, string* pname)
{
- if (sname.find("user.") != 0) {
+ if (!userstring.empty() && sname.find(userstring) != 0) {
errno = EINVAL;
return false;
}
- *pname = sname.substr(cstr_userstring.length());
+ *pname = sname.substr(userstring.length());
return true;
}
} // namespace pxattr
-#else // Testing / driver ->
+#else // TEST_PXATTR Testing / driver ->
#include <stdio.h>
#include <stdlib.h>
@@ -474,35 +478,16 @@
#include <fcntl.h>
#include <errno.h>
#include <string.h>
+#include <ftw.h>
#include <iostream>
+#include <fstream>
+#include <map>
+#include <algorithm>
+#include <string>
+using namespace std;
#include "pxattr.h"
-
-static char *thisprog;
-static char usage [] =
-"pxattr [-h] -n name [-v value] pathname...\n"
-"pxattr [-h] -x name pathname...\n"
-"pxattr [-h] -l pathname...\n"
-" [-h] : don't follow symbolic links (act on link itself)\n"
-"pxattr -T: run tests on temp file in current directory"
-"\n"
-;
-static void
-Usage(void)
-{
- fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
- exit(1);
-}
-
-static int op_flags;
-#define OPT_MOINS 0x1
-#define OPT_n 0x2
-#define OPT_v 0x4
-#define OPT_h 0x8
-#define OPT_x 0x10
-#define OPT_l 0x20
-#define OPT_T 0x40
static void dotests()
{
@@ -627,22 +612,103 @@
exit(0);
}
+// \-quote character c in input \ -> \\, nl -> \n cr -> \rc -> \c
+static void quote(const string& in, string& out, int c)
+{
+ out.clear();
+ for (string::const_iterator it = in.begin(); it != in.end(); it++) {
+ if (*it == '\\') {
+ out += "\\\\";
+ } else if (*it == "\n"[0]) {
+ out += "\\n";
+ } else if (*it == "\r"[0]) {
+ out += "\\r";
+ } else if (*it == c) {
+ out += "\\";
+ out += c;
+ } else {
+ out += *it;
+ }
+ }
+}
+
+// \-unquote input \n -> nl, \r -> cr, \c -> c
+static void unquote(const string& in, string& out)
+{
+ out.clear();
+ for (unsigned int i = 0; i < in.size(); i++) {
+ if (in[i] == '\\') {
+ if (i == in.size() -1) {
+ out += in[i];
+ } else {
+ int c = in[++i];
+ switch (c) {
+ case 'n': out += "\n";break;
+ case 'r': out += "\r";break;
+ default: out += c;
+ }
+ }
+ } else {
+ out += in[i];
+ }
+ }
+}
+
+// Find first unquoted c in input: c preceded by odd number of backslashes
+string::size_type find_first_unquoted(const string& in, int c)
+{
+ int q = 0;
+ for (unsigned int i = 0;i < in.size(); i++) {
+ if (in[i] == '\\') {
+ q++;
+ } else if (in[i] == c) {
+ if (q&1) {
+ // quoted
+ q = 0;
+ } else {
+ return i;
+ }
+ } else {
+ q = 0;
+ }
+ }
+ return string::npos;
+}
+static const string PATH_START("Path: ");
static void listattrs(const string& path)
{
- std::cout << "Path: " << path << std::endl;
vector<string> names;
if (!pxattr::list(path, &names)) {
+ if (errno == ENOENT) {
+ return;
+ }
perror("pxattr::list");
exit(1);
}
+ if (names.empty())
+ return;
+
+ // Sorting the names would not be necessary but it makes easier comparing
+ // backups
+ sort(names.begin(), names.end());
+
+ string quoted;
+ quote(path, quoted, 0);
+ cout << PATH_START << quoted << endl;
for (vector<string>::const_iterator it = names.begin();
it != names.end(); it++) {
string value;
if (!pxattr::get(path, *it, &value)) {
+ if (errno == ENOENT) {
+ return;
+ }
perror("pxattr::get");
exit(1);
}
- std::cout << " " << *it << " => " << value << std::endl;
+ quote(*it, quoted, '=');
+ cout << " " << quoted << "=";
+ quote(value, quoted, 0);
+ cout << quoted << endl;
}
}
@@ -654,15 +720,84 @@
}
}
+// Restore xattrs stored in file created by pxattr -lR output
+static void restore(const char *backupnm)
+{
+ istream *input;
+ ifstream fin;
+ if (!strcmp(backupnm, "stdin")) {
+ input = &cin;
+ } else {
+ fin.open(backupnm, ios::in);
+ input = &fin;
+ }
+
+ bool done = false;
+ int linenum = 0;
+ string path;
+ map<string, string> attrs;
+ while (!done) {
+ string line;
+ getline(*input, line);
+ if (!input->good()) {
+ if (input->bad()) {
+ cerr << "Input I/O error" << endl;
+ exit(1);
+ }
+ done = true;
+ } else {
+ linenum++;
+ }
+
+ // cout << "Got line " << linenum << " : [" << line << "] done " <<
+ // done << endl;
+
+ if (line.find(PATH_START) == 0 || done) {
+ if (!path.empty() && !attrs.empty()) {
+ for (map<string,string>::const_iterator it = attrs.begin();
+ it != attrs.end(); it++) {
+ setxattr(path, it->first, it->second);
+ }
+ }
+ if (!done) {
+ line = line.substr(PATH_START.size(), string::npos);
+ unquote(line, path);
+ attrs.clear();
+ }
+ } else if (line.empty()) {
+ continue;
+ } else {
+ // Should be attribute line
+ if (line[0] != ' ') {
+ cerr << "Found bad line (no space) at " << linenum << endl;
+ exit(1);
+ }
+ string::size_type pos = find_first_unquoted(line, '=');
+ if (pos == string::npos || pos < 2 || pos >= line.size()) {
+ cerr << "Found bad line at " << linenum << endl;
+ exit(1);
+ }
+ string qname = line.substr(1, pos-1);
+ pair<string,string> entry;
+ unquote(qname, entry.first);
+ unquote(line.substr(pos+1), entry.second);
+ attrs.insert(entry);
+ }
+ }
+}
+
void printxattr(const string &path, const string& name)
{
- std::cout << "Path: " << path << std::endl;
+ cout << "Path: " << path << endl;
string value;
if (!pxattr::get(path, name, &value)) {
+ if (errno == ENOENT) {
+ return;
+ }
perror("pxattr::get");
exit(1);
}
- std::cout << " " << name << " => " << value << std::endl;
+ cout << " " << name << " => " << value << endl;
}
void delxattr(const string &path, const string& name)
@@ -673,61 +808,134 @@
}
}
+static char *thisprog;
+static char usage [] =
+"pxattr [-h] -n name pathname [...] : show value for name\n"
+"pxattr [-h] -n name -v value pathname [...] : add/replace attribute\n"
+"pxattr [-h] -x name pathname [...] : delete attribute\n"
+"pxattr [-h] [-l] [-R] pathname [...] : list attribute names and values\n"
+" [-h] : don't follow symbolic links (act on link itself)\n"
+" [-R] : recursive listing. Args should be directory(ies)\n"
+" For all the options above, if no pathname arguments are given, pxattr\n"
+" will read file names on stdin, one per line.\n"
+"pxattr -S <backupfile> Restore xattrs from file created by pxattr -lR output\n"
+" if backupfile is 'stdin', reads from stdin\n"
+"pxattr -T: run tests on temp file in current directory"
+"\n"
+;
+static void
+Usage(void)
+{
+ fprintf(stderr, "%s: usage:\n%s", thisprog, usage);
+ exit(1);
+}
+
+static int op_flags;
+#define OPT_MOINS 0x1
+#define OPT_n 0x2
+#define OPT_v 0x4
+#define OPT_h 0x8
+#define OPT_x 0x10
+#define OPT_l 0x20
+#define OPT_T 0x40
+#define OPT_R 0x80
+#define OPT_S 0x100
+
+static string name, value;
+
+int processfile(const char* fn, const struct stat *sb, int typeflag)
+{
+ //cout << "processfile " << fn << " opflags " << op_flags << endl;
+
+ if (op_flags & OPT_l) {
+ listattrs(fn);
+ } else if (op_flags & OPT_n) {
+ if (op_flags & OPT_v) {
+ setxattr(fn, name, value);
+ } else {
+ printxattr(fn, name);
+ }
+ } else if (op_flags & OPT_x) {
+ delxattr(fn, name);
+ }
+ return 0;
+}
int main(int argc, char **argv)
{
- thisprog = argv[0];
- argc--; argv++;
-
- string name, value;
-
- while (argc > 0 && **argv == '-') {
- (*argv)++;
- if (!(**argv))
- /* Cas du "adb - core" */
- Usage();
- while (**argv)
- switch (*(*argv)++) {
- case 'T': op_flags |= OPT_T; break;
- case 'l': op_flags |= OPT_l; break;
- case 'x': op_flags |= OPT_x; if (argc < 2) Usage();
- name = *(++argv); argc--;
- goto b1;
- case 'n': op_flags |= OPT_n; if (argc < 2) Usage();
- name = *(++argv); argc--;
- goto b1;
- case 'v': op_flags |= OPT_v; if (argc < 2) Usage();
- value = *(++argv); argc--;
- goto b1;
- default: Usage(); break;
- }
- b1: argc--; argv++;
- }
-
- if (argc < 1 && !(op_flags & OPT_T))
- Usage();
- if (op_flags & OPT_l) {
- while (argc > 0) {
- listattrs(*argv++);argc--;
- }
- } else if (op_flags & OPT_n) {
- if (op_flags & OPT_v) {
- while (argc > 0) {
- setxattr(*argv++, name, value);argc--;
- }
- } else {
- while (argc > 0) {
- printxattr(*argv++, name);argc--;
- }
- }
- } else if (op_flags & OPT_x) {
- while (argc > 0) {
- delxattr(*argv++, name);argc--;
- }
- } else if (op_flags & OPT_T) {
- dotests();
- }
- exit(0);
+ thisprog = argv[0];
+ argc--; argv++;
+
+ while (argc > 0 && **argv == '-') {
+ (*argv)++;
+ if (!(**argv))
+ /* Cas du "adb - core" */
+ Usage();
+ while (**argv)
+ switch (*(*argv)++) {
+ case 'l': op_flags |= OPT_l; break;
+ case 'n': op_flags |= OPT_n; if (argc < 2) Usage();
+ name = *(++argv); argc--;
+ goto b1;
+ case 'R': op_flags |= OPT_R; break;
+ case 'S': op_flags |= OPT_S; break;
+ case 'T': op_flags |= OPT_T; break;
+ case 'v': op_flags |= OPT_v; if (argc < 2) Usage();
+ value = *(++argv); argc--;
+ goto b1;
+ case 'x': op_flags |= OPT_x; if (argc < 2) Usage();
+ name = *(++argv); argc--;
+ goto b1;
+ default: Usage(); break;
+ }
+ b1: argc--; argv++;
+ }
+
+ if (op_flags & OPT_T) {
+ if (argc > 0)
+ Usage();
+ dotests();
+ exit(0);
+ }
+
+ if (op_flags & OPT_S) {
+ if (argc != 1)
+ Usage();
+ restore(argv[0]);
+ exit(0);
+ }
+
+ // Default option is 'list'
+ if ((op_flags&(OPT_l|OPT_n|OPT_x)) == 0)
+ op_flags |= OPT_l;
+
+ bool readstdin = false;
+ if (argc == 0)
+ readstdin = true;
+
+ for (;;) {
+ const char *fn = 0;
+ if (argc > 0) {
+ fn = *argv++;
+ argc--;
+ } else if (readstdin) {
+ static char filename[1025];
+ if (!fgets(filename, 1024, stdin))
+ break;
+ filename[strlen(filename)-1] = 0;
+ fn = filename;
+ } else
+ break;
+
+ if (op_flags & OPT_R) {
+ if (ftw(fn, processfile, 20))
+ exit(1);
+ } else {
+ processfile(fn, 0, 0);
+ }
+ }
+
+ exit(0);
}