2 * $Id: asingle.c,v 1.8 2002-02-16 17:12:53 srittau Exp $
7 #endif /* HAVE_CONFIG_H */
12 #include <sys/param.h>
15 #endif /* HAVE_FCNTL_H */
22 #endif /* HAVE_UNISTD_H */
23 #include <atalk/adouble.h>
24 #include <netatalk/endian.h>
28 /* String used to indicate standard input instead of a disk
29 file. Should be a string not normally used for a file
40 /* This structure holds an entry description, consisting of three
41 four byte entities. The first is the Entry ID, the second is
42 the File Offset and the third is the Length.
46 /* Both input and output routines use this struct and the
47 following globals; therefore this module can only be used
48 for one of the two functions at a time.
50 struct single_file_data {
52 char path[ MAXPATHLEN + 1];
53 struct ad_entry entry[ ADEID_MAX ];
56 extern char *forkname[];
57 u_char header_buf[ AD_HEADER_LEN ];
60 * single_open must be called first. pass it a filename that is supposed
61 * to contain a AppleSingle file. an single struct will be allocated and
62 * somewhat initialized; single_filed is set.
65 int single_open( singlefile, flags, fh, options )
72 if ( flags == O_RDONLY ) {
73 if ( strcmp( singlefile, STDIN ) == 0 ) {
74 single.filed = fileno( stdin );
75 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
79 strncpy( single.path, singlefile, MAXPATHLEN );
81 fprintf( stderr, "opened %s for read\n", single.path );
83 if ((( rc = single_header_test()) > 0 ) &&
84 ( single_header_read( fh, rc ) == 0 )) {
94 * single_close must be called before a second file can be opened using
95 * single_open. Upon successful completion, a value of 0 is returned.
96 * Otherwise, a value of -1 is returned.
99 int single_close( keepflag )
102 if ( keepflag == KEEP ) {
103 return( close( single.filed ));
104 } else if ( keepflag == TRASH ) {
105 if (( strcmp( single.path, STDIN ) != 0 ) &&
106 ( unlink( single.path ) < 0 )) {
107 perror ( single.path );
114 * single_header_read is called by single_open, and before any information
115 * can read from the fh substruct. it must be called before any of the
116 * bytes of the other two forks can be read, as well.
119 int single_header_read( fh, version )
124 * entry_buf is used for reading in entry descriptors, and for reading in
125 * the actual entries of FILEINFO, FINDERINFO, and DATES.
127 u_char entry_buf[ 16 ];
129 u_int32_t time_seconds;
130 u_short mask = 0xfcee;
138 * Go through and initialize the array of entry_info structs. Read in the
139 * number of entries, and then read in the info for each entry and save it
143 for ( n = 0 ; n < ADEID_MAX; n++ ) {
144 single.entry[ n ].ade_off = 0;
145 single.entry[ n ].ade_len = 0;
147 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
148 num_entries = ntohs( num_entries );
150 fprintf( stderr, "The number of entries is %d\n", num_entries );
152 for ( ; num_entries > 0 ; num_entries-- ) {
153 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
155 perror( "Premature end of file :" );
158 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
159 entry_id = ntohl( entry_id );
160 memcpy(&single.entry[ entry_id ].ade_off,
162 sizeof( single.entry[ entry_id ].ade_off ));
163 single.entry[ entry_id ].ade_off =
164 ntohl( single.entry[ entry_id ].ade_off );
165 memcpy(&single.entry[ entry_id ].ade_len,
167 sizeof( single.entry[ entry_id ].ade_len ));
168 single.entry[ entry_id ].ade_len =
169 ntohl( single.entry[ entry_id ].ade_len );
171 fprintf( stderr, "entry_id\t%d\n", entry_id );
172 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
173 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
178 * Now that the entries have been identified, check to make sure
179 * it is a Macintosh file if dealing with version two format file.
182 if ( version == 1 ) {
183 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
184 date_entry = ADEID_FILEI;
188 } else if ( version == 2 ) {
189 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
190 date_entry = ADEID_FILEDATESI;
196 fprintf( stderr, "date_entry = %d\n", date_entry );
200 * Go through and copy all the information you can get from
201 * the informational entries into the fh struct. The ENTRYID_DATA
202 * must be the last one done, because it leaves the file pointer in
203 * the right place for the first read of the data fork.
206 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
207 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
210 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
212 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
213 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
214 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
215 perror( "Premature end of file :" );
219 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
220 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
221 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
224 pos = lseek( single.filed,
225 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
226 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
227 sizeof( entry_buf )) {
228 perror( "Premature end of file :" );
231 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
232 sizeof( fh->finder_info.fdType ));
233 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
234 sizeof( fh->finder_info.fdCreator ));
235 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
236 sizeof( fh->finder_info.fdFlags ));
237 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
238 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
239 sizeof( fh->finder_info.fdLocation ));
240 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
241 sizeof( fh->finder_info.fdFldr ));
242 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
243 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
250 strncpy( type, &fh->finder_info.fdType, 4 );
251 strncpy( creator, &fh->finder_info.fdCreator, 4 );
252 type[4] = creator[4] = '\0';
253 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
257 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
258 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
259 fh->comment[0] = '\0';
261 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
263 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
264 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
265 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
266 perror( "Premature end of file :" );
271 * If date_entry is 7, we have an AppleSingle version one, do the
272 * appropriate stuff. If it is 8, we have an AppleSingle version two,
273 * do the right thing. If date_entry is neither, just use the current date.
274 * Unless I can't get the current date, in which case use time zero.
276 if (( date_entry < 7 ) || ( date_entry > 8 )) {
277 if (( time_seconds = time( NULL )) == -1 ) {
278 time_seconds = AD_DATE_START;
280 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
282 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
283 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
284 fh->backup_date = AD_DATE_START;
285 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
286 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
289 } else if ( date_entry == ADEID_FILEI ) {
290 pos = lseek( single.filed,
291 single.entry[ date_entry ].ade_off, SEEK_SET );
292 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
293 sizeof( entry_buf )) {
294 perror( "Premature end of file :" );
297 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
298 sizeof( fh->create_date ));
299 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
300 sizeof( fh->mod_date ));
301 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
302 sizeof(fh->backup_date));
303 } else if ( date_entry == ADEID_FILEDATESI ) {
304 pos = lseek( single.filed,
305 single.entry[ date_entry ].ade_off, SEEK_SET );
306 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
307 sizeof( entry_buf )) {
308 perror( "Premature end of file :" );
311 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
312 sizeof( fh->create_date ));
313 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
314 sizeof( fh->mod_date ));
315 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
316 sizeof(fh->backup_date));
318 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
319 fh->forklen[ ADEID_RFORK ] = 0;
321 fh->forklen[ ADEID_RFORK ] =
322 htonl( single.entry[ ADEID_RFORK ].ade_len );
324 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
325 fh->forklen[ DATA ] = 0;
327 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
328 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
335 * single_header_test is called from single_open. It checks certain
336 * values of the file and determines if the file is an AppleSingle version
337 * one file something else, and returns a one, or negative one to indicate
340 * The Magic Number of the file, the first four bytes, must be hex
341 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
342 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
343 * are only interested in files from Macs. Therefore these bytes must
344 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
345 * "Macintosh " (that is seven blanks of padding).
347 #define MACINTOSH "Macintosh "
348 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
349 0, 0, 0, 0, 0, 0, 0, 0 };
351 int single_header_test(void)
356 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
357 if ( cc < sizeof( header_buf )) {
358 perror( "Premature end of file :" );
362 memcpy( &templong, header_buf, sizeof( templong ));
363 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
364 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
368 memcpy(&templong, header_buf + 4, sizeof( templong ));
369 templong = ntohl( templong );
370 if ( templong == AD_VERSION1 ) {
372 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
374 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
378 } else if ( templong == AD_VERSION2 ) {
380 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
383 "Warning: %s may be a corrupt AppleSingle file.\n",
388 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
397 * single_read is called until it returns zero for each fork. When
398 * it returns zero for the first fork, it seeks to the proper place
399 * to read in the next, if there is one. single_read must be called
400 * enough times to return zero for each fork and no more.
404 int single_read( fork, buffer, length )
417 entry_id = ADEID_DFORK;
420 entry_id = ADEID_RFORK;
427 if ( single.entry[ entry_id ].ade_len < 0 ) {
428 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
429 return( single.entry[ entry_id ].ade_len );
431 if ( single.entry[ entry_id ].ade_len == 0 ) {
432 if ( fork == DATA ) {
433 pos = lseek( single.filed,
434 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
439 if ( single.entry[ entry_id ].ade_len < length ) {
440 readlen = single.entry[ entry_id ].ade_len;
446 while (( readlen > 0 ) && ( cc > 0 )) {
447 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
453 cc = buf_ptr - buffer;
454 single.entry[ entry_id ].ade_len -= cc;