]> arthur.barton.de Git - netatalk.git/blob - bin/afile/achfile.c
Enhanced machine type
[netatalk.git] / bin / afile / achfile.c
1 /*
2  * $Id: achfile.c,v 1.7 2009-10-14 01:38:28 didg Exp $
3  *
4     afile - determine the MacOS creator/type of files
5
6     Copyright (C) 2001 Sebastian Rittau.
7     All rights reserved.
8
9     This file may be distributed and/or modfied under the terms of the
10     following license:
11
12     Redistribution and use in source and binary forms, with or without
13     modification, are permitted provided that the following conditions
14     are met:
15     1. Redistributions of source code must retain the above copyright
16        notice, this list of conditions and the following disclaimer.
17     2. Redistributions in binary form must reproduce the above copyright
18        notice, this list of conditions and the following disclaimer in the
19        documentation and/or other materials provided with the distribution.
20     3. Neither the name of the author nor the names of its contributors
21        may be used to endorse or promote products derived from this software
22        without specific prior written permission.
23
24     THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27     ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34     SUCH DAMAGE.
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif /* HAVE_CONFIG_H */
40
41 #include <stdio.h>
42 #include <string.h>
43 #include <errno.h>
44
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #ifdef HAVE_FCNTL_H
48 #include <fcntl.h>
49 #endif /* HAVE_FCNTL_H */
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif /* HAVE_UNISTD_H */
53
54 #include <atalk/adouble.h>
55
56 #include "common.h"
57
58 /* Global Variables */
59 static const char *type    = NULL;
60 static const char *creator = NULL;
61
62
63 /* Print usage information. */
64 static void usage(char *prog)
65 {
66   fprintf(stderr, "Usage: %s [-t TYPE] [-c CREATOR] FILE ...\n", prog);
67 }
68
69 /* Print extensive help. */
70 static void help(char *prog)
71 {
72   usage(prog);
73   fprintf(stderr,
74           "\n"
75           "Change the MacOS creator/type of FILEs.\n"
76           "\n"
77           "  -t, --type=TYPE        choose type\n"
78           "  -c, --creator=CREATOR  choose creator\n"
79           "  -h, --help             show this help and exit\n"
80           "  -v, --version          show version information and exit\n");
81 }
82
83 /* Print the version. */
84 static void version(void)
85 {
86   fprintf(stderr, "achfile (netatalk " VERSION ")\n");
87 }
88
89 /* Argument Handling
90  * known options: -t, -c, -h, -v
91  * known long options: --help, --version
92  */
93 #define OPTSTRING "t:c:hv-:"
94 static const char *get_long_arg(int argc, char *argv[], const char *arg, const char *oa) {
95   switch (*oa) {
96   case '=':
97     return &oa[1];
98   case '\0':
99     if (optind == argc) {
100       fprintf(stderr, "%s: option \'%s\' requires an argument\n", argv[0], arg);
101       return NULL;
102     }
103     return argv[optind++];
104   default:
105     fprintf(stderr, "%s: unrecognized option \'%s\'\n", argv[0], arg);
106     usage(argv[0]);
107     return NULL;
108   }
109 }
110
111 static int parse_args(int argc, char *argv[])
112 {
113   int c;
114   const char *longarg;
115
116   /* iterate through the command line */
117   while ((c = getopt(argc, argv, OPTSTRING)) != -1) {
118     switch (c) {
119     case 'h':
120       help(argv[0]);
121       exit(0);
122     case 'v':
123       version();
124       exit(0);
125     case 't':
126       type = optarg;
127       break;
128     case 'c':
129       creator = optarg;
130       break;
131     case '-':
132       if (strcmp(optarg, "help") == 0) {
133         help(argv[0]);
134         exit(0);
135       }
136       if (strcmp(optarg, "version") == 0) {
137         version();
138         exit(0);
139       }
140       if (strncmp(optarg, "type", 4) == 0) {
141         longarg = get_long_arg(argc, argv, "type", &optarg[4]);
142         if (!longarg)
143           return -1;
144         type = longarg;
145       } else if (strncmp(optarg, "creator", 7) == 0) {
146         longarg = get_long_arg(argc, argv, "creator", &optarg[7]);
147         if (!longarg)
148           return -1;
149         creator = longarg;
150       }
151       break;
152     default:
153       usage(argv[0]);
154       return -1;
155     }
156   }
157
158   /* At least one file argument is required. */
159   if (argc == optind) {
160     usage(argv[0]);
161     return -1;
162   }
163
164   /* Either type or creator is required. */
165   if (!type && !creator) {
166     fprintf(stderr, "achfile: either type or creator must be specified\n");
167     return -1;
168   }
169
170   /* Type and creator must be exactly four characters long. */
171   if ((type && strlen(type) != 4) || (creator && strlen(creator) != 4)) {
172     fprintf(stderr, "achfile: type and creator must be four character IDs\n");
173     return -1;
174   }
175
176   return 0;
177 }
178
179
180 /* Change the owner/creator of each file specified on the command line. */
181 static int handle_file(const char *filename)
182 {
183   int fd;
184   struct stat statbuf;
185   char *adname;
186   ssize_t sz;
187   char buf[AD_DATASZ];
188   struct adouble *ad = (struct adouble *) &buf;
189
190   if (stat(filename, &statbuf) == -1) {
191     fprintf(stderr, "achfile:%s: %s\n", filename, strerror(errno));
192     return -1;
193   }
194
195   adname = dataname_to_adname(filename);
196   fd = open(adname, O_RDWR, 0);
197   if (fd == -1) {
198     if (errno == ENOENT)
199       fprintf(stderr, "achfile:%s: no resource fork\n", filename);
200     else
201       fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
202     free(adname);
203     return -1;
204   }
205   sz = read(fd, buf, AD_DATASZ);
206   if (sz == -1) {
207     fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
208     free(adname);
209     close(fd);
210     return -1;
211   } else if (sz < AD_DATASZ) {
212     fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
213     free(adname);
214     close(fd);
215     return -1;
216   }
217   if ( ntohl(ad->ad_magic) != AD_MAGIC) {
218     fprintf(stderr, "achfile:%s: corrupt resource fork\n", filename);
219     free(adname);
220     close(fd);
221     return -1;
222   }
223
224   /* Change Type */
225   if (type) {
226     buf[ADEDOFF_FINDERI + 0] = type[0];
227     buf[ADEDOFF_FINDERI + 1] = type[1];
228     buf[ADEDOFF_FINDERI + 2] = type[2];
229     buf[ADEDOFF_FINDERI + 3] = type[3];
230  }
231
232   /* Change Creator */
233   if (creator) {
234     buf[ADEDOFF_FINDERI + 4] = creator[0];
235     buf[ADEDOFF_FINDERI + 5] = creator[1];
236     buf[ADEDOFF_FINDERI + 6] = creator[2];
237     buf[ADEDOFF_FINDERI + 7] = creator[3];
238  }
239
240   /* Write file back to disk. */
241   if (lseek(fd, 0, SEEK_SET) == -1) {
242     fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
243     free(adname);
244     close(fd);
245     return -1;
246   }
247   if (write(fd, &buf, AD_DATASZ) < AD_DATASZ) {
248     fprintf(stderr, "achfile:%s: %s\n", adname, strerror(errno));
249     free(adname);
250     close(fd);
251     return -1;
252   }
253
254   /* Clean Up */
255   free(adname);
256   close(fd);
257
258   return 0;
259 }
260
261
262 int main(int argc, char *argv[])
263 {
264   /* argument handling */
265   if (parse_args(argc, argv) == -1)
266     return 1;
267
268   while (optind < argc)
269     handle_file(argv[optind++]);
270
271   return 0;
272 }