16 #include <atalk/adouble.h>
17 #include <netatalk/endian.h>
20 int single_header_read(struct FHeader *fh, int version);
21 int single_close(int readflag);
22 int single_header_test(void);
24 /* String used to indicate standard input instead of a disk
25 file. Should be a string not normally used for a file
36 /* This structure holds an entry description, consisting of three
37 four byte entities. The first is the Entry ID, the second is
38 the File Offset and the third is the Length.
42 /* Both input and output routines use this struct and the
43 following globals; therefore this module can only be used
44 for one of the two functions at a time.
46 struct single_file_data {
48 char path[ MAXPATHLEN + 1];
49 struct ad_entry entry[ ADEID_MAX ];
52 extern char *forkname[];
53 u_char header_buf[ AD_HEADER_LEN ];
56 * single_open must be called first. pass it a filename that is supposed
57 * to contain a AppleSingle file. an single struct will be allocated and
58 * somewhat initialized; single_filed is set.
61 int single_open( singlefile, flags, fh, options )
68 if ( flags == O_RDONLY ) {
69 if ( strcmp( singlefile, STDIN ) == 0 ) {
70 single.filed = fileno( stdin );
71 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
75 strncpy( single.path, singlefile, MAXPATHLEN );
77 fprintf( stderr, "opened %s for read\n", single.path );
79 if ((( rc = single_header_test()) > 0 ) &&
80 ( single_header_read( fh, rc ) == 0 )) {
90 * single_close must be called before a second file can be opened using
91 * single_open. Upon successful completion, a value of 0 is returned.
92 * Otherwise, a value of -1 is returned.
95 int single_close( keepflag )
98 if ( keepflag == KEEP ) {
99 return( close( single.filed ));
100 } else if ( keepflag == TRASH ) {
101 if (( strcmp( single.path, STDIN ) != 0 ) &&
102 ( unlink( single.path ) < 0 )) {
103 perror ( single.path );
110 * single_header_read is called by single_open, and before any information
111 * can read from the fh substruct. it must be called before any of the
112 * bytes of the other two forks can be read, as well.
115 int single_header_read( fh, version )
120 * entry_buf is used for reading in entry descriptors, and for reading in
121 * the actual entries of FILEINFO, FINDERINFO, and DATES.
123 u_char entry_buf[ 16 ];
125 u_int32_t time_seconds;
126 u_short mask = 0xfcee;
134 * Go through and initialize the array of entry_info structs. Read in the
135 * number of entries, and then read in the info for each entry and save it
139 for ( n = 0 ; n < ADEID_MAX; n++ ) {
140 single.entry[ n ].ade_off = 0;
141 single.entry[ n ].ade_len = 0;
143 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
144 num_entries = ntohs( num_entries );
146 fprintf( stderr, "The number of entries is %d\n", num_entries );
148 for ( ; num_entries > 0 ; num_entries-- ) {
149 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
151 perror( "Premature end of file :" );
154 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
155 entry_id = ntohl( entry_id );
156 memcpy(&single.entry[ entry_id ].ade_off,
158 sizeof( single.entry[ entry_id ].ade_off ));
159 single.entry[ entry_id ].ade_off =
160 ntohl( single.entry[ entry_id ].ade_off );
161 memcpy(&single.entry[ entry_id ].ade_len,
163 sizeof( single.entry[ entry_id ].ade_len ));
164 single.entry[ entry_id ].ade_len =
165 ntohl( single.entry[ entry_id ].ade_len );
167 fprintf( stderr, "entry_id\t%d\n", entry_id );
168 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
169 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
174 * Now that the entries have been identified, check to make sure
175 * it is a Macintosh file if dealing with version two format file.
178 if ( version == 1 ) {
179 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
180 date_entry = ADEID_FILEI;
184 } else if ( version == 2 ) {
185 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
186 date_entry = ADEID_FILEDATESI;
192 fprintf( stderr, "date_entry = %d\n", date_entry );
196 * Go through and copy all the information you can get from
197 * the informational entries into the fh struct. The ENTRYID_DATA
198 * must be the last one done, because it leaves the file pointer in
199 * the right place for the first read of the data fork.
202 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
203 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
206 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
208 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
209 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
210 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
211 perror( "Premature end of file :" );
215 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
216 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
217 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
220 pos = lseek( single.filed,
221 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
222 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
223 sizeof( entry_buf )) {
224 perror( "Premature end of file :" );
227 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
228 sizeof( fh->finder_info.fdType ));
229 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
230 sizeof( fh->finder_info.fdCreator ));
231 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
232 sizeof( fh->finder_info.fdFlags ));
233 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
234 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
235 sizeof( fh->finder_info.fdLocation ));
236 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
237 sizeof( fh->finder_info.fdFldr ));
238 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
239 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
246 strncpy( type, &fh->finder_info.fdType, 4 );
247 strncpy( creator, &fh->finder_info.fdCreator, 4 );
248 type[4] = creator[4] = '\0';
249 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
253 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
254 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
255 fh->comment[0] = '\0';
257 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
259 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
260 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
261 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
262 perror( "Premature end of file :" );
267 * If date_entry is 7, we have an AppleSingle version one, do the
268 * appropriate stuff. If it is 8, we have an AppleSingle version two,
269 * do the right thing. If date_entry is neither, just use the current date.
270 * Unless I can't get the current date, in which case use time zero.
272 if (( date_entry < 7 ) || ( date_entry > 8 )) {
273 if (( time_seconds = time( NULL )) == -1 ) {
274 time_seconds = AD_DATE_START;
276 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
278 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
279 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
280 fh->backup_date = AD_DATE_START;
281 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
282 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
285 } else if ( date_entry == ADEID_FILEI ) {
286 pos = lseek( single.filed,
287 single.entry[ date_entry ].ade_off, SEEK_SET );
288 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
289 sizeof( entry_buf )) {
290 perror( "Premature end of file :" );
293 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
294 sizeof( fh->create_date ));
295 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
296 sizeof( fh->mod_date ));
297 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
298 sizeof(fh->backup_date));
299 } else if ( date_entry == ADEID_FILEDATESI ) {
300 pos = lseek( single.filed,
301 single.entry[ date_entry ].ade_off, SEEK_SET );
302 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
303 sizeof( entry_buf )) {
304 perror( "Premature end of file :" );
307 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
308 sizeof( fh->create_date ));
309 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
310 sizeof( fh->mod_date ));
311 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
312 sizeof(fh->backup_date));
314 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
315 fh->forklen[ ADEID_RFORK ] = 0;
317 fh->forklen[ ADEID_RFORK ] =
318 htonl( single.entry[ ADEID_RFORK ].ade_len );
320 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
321 fh->forklen[ DATA ] = 0;
323 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
324 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
331 * single_header_test is called from single_open. It checks certain
332 * values of the file and determines if the file is an AppleSingle version
333 * one file something else, and returns a one, or negative one to indicate
336 * The Magic Number of the file, the first four bytes, must be hex
337 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
338 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
339 * are only interested in files from Macs. Therefore these bytes must
340 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
341 * "Macintosh " (that is seven blanks of padding).
343 #define MACINTOSH "Macintosh "
344 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
345 0, 0, 0, 0, 0, 0, 0, 0 };
347 int single_header_test(void)
352 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
353 if ( cc < sizeof( header_buf )) {
354 perror( "Premature end of file :" );
358 memcpy( &templong, header_buf, sizeof( templong ));
359 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
360 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
364 memcpy(&templong, header_buf + 4, sizeof( templong ));
365 templong = ntohl( templong );
366 if ( templong == AD_VERSION1 ) {
368 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
370 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
374 } else if ( templong == AD_VERSION2 ) {
376 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
379 "Warning: %s may be a corrupt AppleSingle file.\n",
384 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
393 * single_read is called until it returns zero for each fork. When
394 * it returns zero for the first fork, it seeks to the proper place
395 * to read in the next, if there is one. single_read must be called
396 * enough times to return zero for each fork and no more.
400 int single_read( fork, buffer, length )
413 entry_id = ADEID_DFORK;
416 entry_id = ADEID_RFORK;
423 if ( single.entry[ entry_id ].ade_len < 0 ) {
424 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
425 return( single.entry[ entry_id ].ade_len );
427 if ( single.entry[ entry_id ].ade_len == 0 ) {
428 if ( fork == DATA ) {
429 pos = lseek( single.filed,
430 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
435 if ( single.entry[ entry_id ].ade_len < length ) {
436 readlen = single.entry[ entry_id ].ade_len;
442 while (( readlen > 0 ) && ( cc > 0 )) {
443 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
449 cc = buf_ptr - buffer;
450 single.entry[ entry_id ].ade_len -= cc;