/*
- * Copyright 1998 The University of Newcastle upon Tyne
- *
- * Permission to use, copy, modify and distribute this software and its
- * documentation for any purpose other than its commercial exploitation
- * is hereby granted without fee, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of The University of Newcastle upon Tyne not be used in
- * advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. The University of
- * Newcastle upon Tyne makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
- *
- * THE UNIVERSITY OF NEWCASTLE UPON TYNE DISCLAIMS ALL WARRANTIES WITH
- * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
- * NEWCASTLE UPON TYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- *
- * Author: Gerry Tomlinson (gerry.tomlinson@newcastle.ac.uk)
- * Computing Science Department, University of Newcastle upon Tyne, UK
- */
+ * $Id: achfile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
+ *
+ afile - determine the MacOS creator/type of files
+ Copyright (C) 2001 Sebastian Rittau.
+ All rights reserved.
+ This file may be distributed and/or modfied under the terms of the
+ following license:
-/*
- *
- * program achfile
- *
- *
- * change creator/type of netatalk file
- *
- * usage achfile [-t type] [-c creator] file ....
- *
- * returns exit status 0 if all files changed successfully
- *
- * by gerry.tomlinson@newcastle.ac.uk
- * last update 26/2/98
- *
- */
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the author nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <strings.h>
+#include <string.h>
+#include <errno.h>
+
#include <sys/types.h>
#include <sys/stat.h>
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif /* HAVE_FCNTL_H */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
#include <atalk/adouble.h>
-extern char *optarg;
-extern int optind, opterr;
+#include "common.h"
-#define AD ".AppleDouble"
+/* Global Variables */
+static const char *type = NULL;
+static const char *creator = NULL;
-main(int argc, char *argv[])
+/* Print usage information. */
+static void usage(char *prog)
+{
+ fprintf(stderr, "Usage: %s [-t TYPE] [-c CREATOR] FILE ...\n", prog);
+}
+/* Print extensive help. */
+static void help(char *prog)
{
- int fdata, fres;
- struct stat statbuf;
- int n,i;
- char rbuf [AD_DATASZ];
- struct adouble *ad = (struct adouble*)&rbuf;
- char *dname;
- char *rname;
- char *slash;
- char c;
- char *type = 0;
- char *creator = 0;
- char *p;
- int errflag = 0;
- int status = 0;
-
- while ((c = getopt(argc, argv, "t:c:")) != -1)
- switch (c) {
- case 't':
- type = optarg;
- if (strlen(type) != 4)
- {fprintf(stderr,"achfile: bad type length\n");exit(1);}
- break;
- case 'c':
- creator = optarg;
- if (strlen(creator) != 4)
- {fprintf(stderr,"achfile: bad creator length\n");exit(1);}
- break;
- case '?' :
- errflag = 1; break;
+ usage(prog);
+ fprintf(stderr,
+ "\n"
+ "Change the MacOS creator/type of FILEs.\n"
+ "\n"
+ " -t, --type=TYPE choose type\n"
+ " -c, --creator=CREATOR choose creator\n"
+ " -h, --help show this help and exit\n"
+ " -v, --version show version information and exit\n");
+}
+
+/* Print the version. */
+static void version(void)
+{
+ fprintf(stderr, "achfile (netatalk " VERSION ")\n");
+}
+
+/* Argument Handling
+ * known options: -t, -c, -h, -v
+ * known long options: --help, --version
+ */
+#define OPTSTRING "t:c:hv-:"
+static const char *get_long_arg(int argc, char *argv[], const char *arg, const char *oa) {
+ switch (*oa) {
+ case '=':
+ return &oa[1];
+ case '\0':
+ if (optind == argc) {
+ fprintf(stderr, "%s: option \'%s\' requires an argument\n", argv[0], arg);
+ return NULL;
}
+ return argv[optind++];
+ default:
+ fprintf(stderr, "%s: unrecognized option \'%s\'\n", argv[0], arg);
+ usage(argv[0]);
+ return NULL;
+ }
+}
+
+static int parse_args(int argc, char *argv[])
+{
+ int c;
+ const char *longarg;
- if (errflag || argc == optind || (!type && !creator) )
- {fprintf(stderr,"usage: achfile [-t type] [-c creator] file ...\n");exit(1);}
-
-
- for (; optind < argc; optind++) {
- close (fdata);
- close (fres);
-
- dname = argv[optind];
-
- if ((fdata = open(dname,O_RDONLY,0)) == -1)
- {fprintf(stderr,"achfile: can't open %s\n",dname); status = 2; continue;}
-
- fstat(fdata, &statbuf);
- if (S_ISDIR(statbuf.st_mode))
- {status = 2; continue;}
-
- rname = calloc(strlen(dname)+strlen(AD)+2, 1);
- slash = rindex(dname,'/');
- strncpy(rname,dname,slash ? slash + 1 - dname : 0);
- strcat(rname,AD);
- strcat(rname,"/");
- strcat(rname,slash ? slash+1 : dname);
-
- if ((fres = open(rname, O_RDWR,0)) == -1)
- {fprintf(stderr, "can't open %s\n",rname); status = 2; continue;}
- if ( read(fres,rbuf,AD_DATASZ) != AD_DATASZ)
- {fprintf(stderr,"can't read %s\n",rname); status = 2; continue;}
- if (ad->ad_magic != AD_MAGIC)
- {fprintf(stderr,"bad MAGIC in %s\n",rname); status = 2 ; continue;}
- if (type)
- for (i=0,p=type;i<4;i++) *(rbuf + ADEDOFF_FINDERI +i) = *p++;
- if (creator)
- for (i=0,p=creator;i<4;i++) *(rbuf + ADEDOFF_FINDERI +4 +i) = *p++;
- lseek(fres,0,0);
- if (write(fres,rbuf,AD_DATASZ) != AD_DATASZ)
- {fprintf(stderr,"can't update %s\n",rname); status = 3; continue;}
-
+ /* iterate through the command line */
+ while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
+ switch (c) {
+ case 'h':
+ help(argv[0]);
+ exit(0);
+ case 'v':
+ version();
+ exit(0);
+ case 't':
+ type = optarg;
+ break;
+ case 'c':
+ creator = optarg;
+ break;
+ case '-':
+ if (strcmp(optarg, "help") == 0) {
+ help(argv[0]);
+ exit(0);
+ }
+ if (strcmp(optarg, "version") == 0) {
+ version();
+ exit(0);
+ }
+ if (strncmp(optarg, "type", 4) == 0) {
+ longarg = get_long_arg(argc, argv, "type", &optarg[4]);
+ if (!longarg)
+ return -1;
+ type = longarg;
+ } else if (strncmp(optarg, "creator", 7) == 0) {
+ longarg = get_long_arg(argc, argv, "creator", &optarg[7]);
+ if (!longarg)
+ return -1;
+ creator = longarg;
+ }
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
}
- exit (0);
+ }
+
+ /* At least one file argument is required. */
+ if (argc == optind) {
+ usage(argv[0]);
+ return -1;
+ }
+
+ /* Either type or creator is required. */
+ if (!type && !creator) {
+ fprintf(stderr, "achfile: either type or creator must be specified\n");
+ return -1;
+ }
+
+ /* Type and creator must be exactly four characters long. */
+ if ((type && strlen(type) != 4) || (creator && strlen(creator) != 4)) {
+ fprintf(stderr, "achfile: type and creator must be four character IDs\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Change the owner/creator of each file specified on the command line. */
+static int handle_file(const char *filename)
+{
+ int fd;
+ struct stat statbuf;
+ char *adname;
+ ssize_t sz;
+ char buf[AD_DATASZ];
+ struct adouble *ad = (struct adouble *) &buf;
+
+ if (stat(filename, &statbuf) == -1) {
+ fprintf(stderr, "achfile:%s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ adname = dataname_to_adname(filename);
+ fd = open(adname, O_RDWR, 0);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ fprintf(stderr, "achfile:%s: no resource fork\n", filename);
+ else
+ fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
+ free(adname);
+ return -1;
+ }
+ sz = read(fd, buf, AD_DATASZ);
+ if (sz == -1) {
+ fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
+ free(adname);
+ close(fd);
+ return -1;
+ } else if (sz < AD_DATASZ) {
+ fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
+ free(adname);
+ close(fd);
+ return -1;
+ }
+ if ( ntohl(ad->ad_magic) != AD_MAGIC) {
+ fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
+ free(adname);
+ close(fd);
+ return -1;
+ }
+
+ /* Change Type */
+ if (type) {
+ buf[ADEDOFF_FINDERI + 0] = type[0];
+ buf[ADEDOFF_FINDERI + 1] = type[1];
+ buf[ADEDOFF_FINDERI + 2] = type[2];
+ buf[ADEDOFF_FINDERI + 3] = type[3];
+ }
+
+ /* Change Creator */
+ if (creator) {
+ buf[ADEDOFF_FINDERI + 4] = creator[0];
+ buf[ADEDOFF_FINDERI + 5] = creator[1];
+ buf[ADEDOFF_FINDERI + 6] = creator[2];
+ buf[ADEDOFF_FINDERI + 7] = creator[3];
+ }
+
+ /* Write file back to disk. */
+ if (lseek(fd, 0, SEEK_SET) == -1) {
+ fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
+ free(adname);
+ close(fd);
+ return -1;
+ }
+ if (write(fd, &buf, AD_DATASZ) < AD_DATASZ) {
+ fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
+ free(adname);
+ close(fd);
+ return -1;
+ }
+
+ /* Clean Up */
+ free(adname);
+ close(fd);
+
+ return 0;
}
-/* end of program achfile */
+
+int main(int argc, char *argv[])
+{
+ /* argument handling */
+ if (parse_args(argc, argv) == -1)
+ return 1;
+
+ while (optind < argc)
+ handle_file(argv[optind++]);
+
+ return 0;
+}