15 #include <atalk/adouble.h>
16 #include <netatalk/endian.h>
21 /* String used to indicate standard input instead of a disk
22 file. Should be a string not normally used for a file
33 /* This structure holds an entry description, consisting of three
34 four byte entities. The first is the Entry ID, the second is
35 the File Offset and the third is the Length.
39 /* Both input and output routines use this struct and the
40 following globals; therefore this module can only be used
41 for one of the two functions at a time.
43 struct single_file_data {
45 char path[ MAXPATHLEN + 1];
46 struct ad_entry entry[ ADEID_MAX ];
49 extern char *forkname[];
50 u_char header_buf[ AD_HEADER_LEN ];
53 * single_open must be called first. pass it a filename that is supposed
54 * to contain a AppleSingle file. an single struct will be allocated and
55 * somewhat initialized; single_filed is set.
58 single_open( singlefile, flags, fh, options )
65 if ( flags == O_RDONLY ) {
66 if ( strcmp( singlefile, STDIN ) == 0 ) {
67 single.filed = fileno( stdin );
68 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
72 strncpy( single.path, singlefile, MAXPATHLEN );
74 fprintf( stderr, "opened %s for read\n", single.path );
76 if ((( rc = single_header_test()) > 0 ) &&
77 ( single_header_read( fh, rc ) == 0 )) {
86 * single_close must be called before a second file can be opened using
87 * single_open. Upon successful completion, a value of 0 is returned.
88 * Otherwise, a value of -1 is returned.
91 single_close( keepflag )
94 if ( keepflag == KEEP ) {
95 return( close( single.filed ));
96 } else if ( keepflag == TRASH ) {
97 if (( strcmp( single.path, STDIN ) != 0 ) &&
98 ( unlink( single.path ) < 0 )) {
99 perror ( single.path );
106 * single_header_read is called by single_open, and before any information
107 * can read from the fh substruct. it must be called before any of the
108 * bytes of the other two forks can be read, as well.
111 single_header_read( fh, version )
116 * entry_buf is used for reading in entry descriptors, and for reading in
117 * the actual entries of FILEINFO, FINDERINFO, and DATES.
119 u_char entry_buf[ 16 ];
121 u_int32_t time_seconds;
122 u_short mask = 0xfcee;
130 * Go through and initialize the array of entry_info structs. Read in the
131 * number of entries, and then read in the info for each entry and save it
135 for ( n = 0 ; n < ADEID_MAX; n++ ) {
136 single.entry[ n ].ade_off = 0;
137 single.entry[ n ].ade_len = 0;
139 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
140 num_entries = ntohs( num_entries );
142 fprintf( stderr, "The number of entries is %d\n", num_entries );
144 for ( ; num_entries > 0 ; num_entries-- ) {
145 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
147 perror( "Premature end of file :" );
150 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
151 entry_id = ntohl( entry_id );
152 memcpy(&single.entry[ entry_id ].ade_off,
154 sizeof( single.entry[ entry_id ].ade_off ));
155 single.entry[ entry_id ].ade_off =
156 ntohl( single.entry[ entry_id ].ade_off );
157 memcpy(&single.entry[ entry_id ].ade_len,
159 sizeof( single.entry[ entry_id ].ade_len ));
160 single.entry[ entry_id ].ade_len =
161 ntohl( single.entry[ entry_id ].ade_len );
163 fprintf( stderr, "entry_id\t%d\n", entry_id );
164 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
165 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
170 * Now that the entries have been identified, check to make sure
171 * it is a Macintosh file if dealing with version two format file.
174 if ( version == 1 ) {
175 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
176 date_entry = ADEID_FILEI;
180 } else if ( version == 2 ) {
181 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
182 date_entry = ADEID_FILEDATESI;
188 fprintf( stderr, "date_entry = %d\n", date_entry );
192 * Go through and copy all the information you can get from
193 * the informational entries into the fh struct. The ENTRYID_DATA
194 * must be the last one done, because it leaves the file pointer in
195 * the right place for the first read of the data fork.
198 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
199 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
202 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
204 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
205 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
206 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
207 perror( "Premature end of file :" );
211 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
212 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
213 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
216 pos = lseek( single.filed,
217 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
218 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
219 sizeof( entry_buf )) {
220 perror( "Premature end of file :" );
223 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
224 sizeof( fh->finder_info.fdType ));
225 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
226 sizeof( fh->finder_info.fdCreator ));
227 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
228 sizeof( fh->finder_info.fdFlags ));
229 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
230 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
231 sizeof( fh->finder_info.fdLocation ));
232 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
233 sizeof( fh->finder_info.fdFldr ));
234 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
235 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
242 strncpy( type, &fh->finder_info.fdType, 4 );
243 strncpy( creator, &fh->finder_info.fdCreator, 4 );
244 type[4] = creator[4] = '\0';
245 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
249 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
250 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
251 fh->comment[0] = '\0';
253 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
255 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
256 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
257 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
258 perror( "Premature end of file :" );
263 * If date_entry is 7, we have an AppleSingle version one, do the
264 * appropriate stuff. If it is 8, we have an AppleSingle version two,
265 * do the right thing. If date_entry is neither, just use the current date.
266 * Unless I can't get the current date, in which case use time zero.
268 if (( date_entry < 7 ) || ( date_entry > 8 )) {
269 if (( time_seconds = time( NULL )) == -1 ) {
270 time_seconds = AD_DATE_START;
272 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
274 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
275 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
276 fh->backup_date = AD_DATE_START;
277 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
278 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
281 } else if ( date_entry == ADEID_FILEI ) {
282 pos = lseek( single.filed,
283 single.entry[ date_entry ].ade_off, SEEK_SET );
284 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
285 sizeof( entry_buf )) {
286 perror( "Premature end of file :" );
289 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
290 sizeof( fh->create_date ));
291 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
292 sizeof( fh->mod_date ));
293 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
294 sizeof(fh->backup_date));
295 } else if ( date_entry == ADEID_FILEDATESI ) {
296 pos = lseek( single.filed,
297 single.entry[ date_entry ].ade_off, SEEK_SET );
298 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
299 sizeof( entry_buf )) {
300 perror( "Premature end of file :" );
303 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
304 sizeof( fh->create_date ));
305 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
306 sizeof( fh->mod_date ));
307 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
308 sizeof(fh->backup_date));
310 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
311 fh->forklen[ ADEID_RFORK ] = 0;
313 fh->forklen[ ADEID_RFORK ] =
314 htonl( single.entry[ ADEID_RFORK ].ade_len );
316 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
317 fh->forklen[ DATA ] = 0;
319 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
320 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
327 * single_header_test is called from single_open. It checks certain
328 * values of the file and determines if the file is an AppleSingle version
329 * one file something else, and returns a one, or negative one to indicate
332 * The Magic Number of the file, the first four bytes, must be hex
333 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
334 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
335 * are only interested in files from Macs. Therefore these bytes must
336 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
337 * "Macintosh " (that is seven blanks of padding).
339 #define MACINTOSH "Macintosh "
340 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
341 0, 0, 0, 0, 0, 0, 0, 0 };
348 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
349 if ( cc < sizeof( header_buf )) {
350 perror( "Premature end of file :" );
354 memcpy( &templong, header_buf, sizeof( templong ));
355 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
356 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
360 memcpy(&templong, header_buf + 4, sizeof( templong ));
361 templong = ntohl( templong );
362 if ( templong == AD_VERSION1 ) {
364 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
366 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
370 } else if ( templong == AD_VERSION2 ) {
372 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
375 "Warning: %s may be a corrupt AppleSingle file.\n",
380 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
389 * single_read is called until it returns zero for each fork. When
390 * it returns zero for the first fork, it seeks to the proper place
391 * to read in the next, if there is one. single_read must be called
392 * enough times to return zero for each fork and no more.
396 single_read( fork, buffer, length )
409 entry_id = ADEID_DFORK;
412 entry_id = ADEID_RFORK;
419 if ( single.entry[ entry_id ].ade_len < 0 ) {
420 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
421 return( single.entry[ entry_id ].ade_len );
423 if ( single.entry[ entry_id ].ade_len == 0 ) {
424 if ( fork == DATA ) {
425 pos = lseek( single.filed,
426 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
431 if ( single.entry[ entry_id ].ade_len < length ) {
432 readlen = single.entry[ entry_id ].ade_len;
438 while (( readlen > 0 ) && ( cc > 0 )) {
439 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
445 cc = buf_ptr - buffer;
446 single.entry[ entry_id ].ade_len -= cc;