15 #include <atalk/adouble.h>
16 #include <netatalk/endian.h>
19 /* String used to indicate standard input instead of a disk
20 file. Should be a string not normally used for a file
31 /* This structure holds an entry description, consisting of three
32 four byte entities. The first is the Entry ID, the second is
33 the File Offset and the third is the Length.
37 /* Both input and output routines use this struct and the
38 following globals; therefore this module can only be used
39 for one of the two functions at a time.
41 struct single_file_data {
43 char path[ MAXPATHLEN + 1];
44 struct ad_entry entry[ ADEID_MAX ];
47 extern char *forkname[];
48 u_char header_buf[ AD_HEADER_LEN ];
51 * single_open must be called first. pass it a filename that is supposed
52 * to contain a AppleSingle file. an single struct will be allocated and
53 * somewhat initialized; single_filed is set.
56 single_open( singlefile, flags, fh, options )
63 if ( flags == O_RDONLY ) {
64 if ( strcmp( singlefile, STDIN ) == 0 ) {
65 single.filed = fileno( stdin );
66 } else if (( single.filed = open( singlefile, flags )) < 0 ) {
70 strncpy( single.path, singlefile, MAXPATHLEN );
72 fprintf( stderr, "opened %s for read\n", single.path );
74 if ((( rc = single_header_test()) > 0 ) &&
75 ( single_header_read( fh, rc ) == 0 )) {
84 * single_close must be called before a second file can be opened using
85 * single_open. Upon successful completion, a value of 0 is returned.
86 * Otherwise, a value of -1 is returned.
89 single_close( keepflag )
92 if ( keepflag == KEEP ) {
93 return( close( single.filed ));
94 } else if ( keepflag == TRASH ) {
95 if (( strcmp( single.path, STDIN ) != 0 ) &&
96 ( unlink( single.path ) < 0 )) {
97 perror ( single.path );
104 * single_header_read is called by single_open, and before any information
105 * can read from the fh substruct. it must be called before any of the
106 * bytes of the other two forks can be read, as well.
109 single_header_read( fh, version )
114 * entry_buf is used for reading in entry descriptors, and for reading in
115 * the actual entries of FILEINFO, FINDERINFO, and DATES.
117 u_char entry_buf[ 16 ];
119 u_int32_t time_seconds;
120 u_short mask = 0xfcee;
128 * Go through and initialize the array of entry_info structs. Read in the
129 * number of entries, and then read in the info for each entry and save it
133 for ( n = 0 ; n < ADEID_MAX; n++ ) {
134 single.entry[ n ].ade_off = 0;
135 single.entry[ n ].ade_len = 0;
137 memcpy( &num_entries, header_buf + 24, sizeof( num_entries ));
138 num_entries = ntohs( num_entries );
140 fprintf( stderr, "The number of entries is %d\n", num_entries );
142 for ( ; num_entries > 0 ; num_entries-- ) {
143 if ( read( single.filed, (char *)entry_buf, AD_ENTRY_LEN )
145 perror( "Premature end of file :" );
148 memcpy(&entry_id, entry_buf, sizeof( entry_id ));
149 entry_id = ntohl( entry_id );
150 memcpy(&single.entry[ entry_id ].ade_off,
152 sizeof( single.entry[ entry_id ].ade_off ));
153 single.entry[ entry_id ].ade_off =
154 ntohl( single.entry[ entry_id ].ade_off );
155 memcpy(&single.entry[ entry_id ].ade_len,
157 sizeof( single.entry[ entry_id ].ade_len ));
158 single.entry[ entry_id ].ade_len =
159 ntohl( single.entry[ entry_id ].ade_len );
161 fprintf( stderr, "entry_id\t%d\n", entry_id );
162 fprintf( stderr, "\toffset\t\t%d\n", single.entry[ entry_id ].ade_off );
163 fprintf( stderr, "\tlength\t\t%d\n", single.entry[ entry_id ].ade_len );
168 * Now that the entries have been identified, check to make sure
169 * it is a Macintosh file if dealing with version two format file.
172 if ( version == 1 ) {
173 if ( single.entry[ ADEID_FILEI ].ade_len > 0 ) {
174 date_entry = ADEID_FILEI;
178 } else if ( version == 2 ) {
179 if ( single.entry[ ADEID_FILEDATESI ].ade_len > 0 ) {
180 date_entry = ADEID_FILEDATESI;
186 fprintf( stderr, "date_entry = %d\n", date_entry );
190 * Go through and copy all the information you can get from
191 * the informational entries into the fh struct. The ENTRYID_DATA
192 * must be the last one done, because it leaves the file pointer in
193 * the right place for the first read of the data fork.
196 if ( single.entry[ ADEID_NAME ].ade_off == 0 ) {
197 fprintf( stderr, "%s has no name for the mac file.\n", single.path );
200 pos = lseek( single.filed, single.entry[ ADEID_NAME ].ade_off,
202 readlen = single.entry[ ADEID_NAME ].ade_len > ADEDLEN_NAME ?
203 ADEDLEN_NAME : single.entry[ ADEID_NAME ].ade_len;
204 if ( read( single.filed, (char *)fh->name, readlen ) != readlen ) {
205 perror( "Premature end of file :" );
209 if (( single.entry[ ADEID_FINDERI ].ade_len < 16 ) ||
210 ( single.entry[ ADEID_FINDERI ].ade_off <= AD_HEADER_LEN )) {
211 fprintf( stderr, "%s has bogus FinderInfo.\n", single.path );
214 pos = lseek( single.filed,
215 single.entry[ ADEID_FINDERI ].ade_off, SEEK_SET );
216 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
217 sizeof( entry_buf )) {
218 perror( "Premature end of file :" );
221 memcpy( &fh->finder_info.fdType, entry_buf + FINDERIOFF_TYPE,
222 sizeof( fh->finder_info.fdType ));
223 memcpy( &fh->finder_info.fdCreator, entry_buf + FINDERIOFF_CREATOR,
224 sizeof( fh->finder_info.fdCreator ));
225 memcpy( &fh->finder_info.fdFlags, entry_buf + FINDERIOFF_FLAGS,
226 sizeof( fh->finder_info.fdFlags ));
227 fh->finder_info.fdFlags = fh->finder_info.fdFlags & mask;
228 memcpy( &fh->finder_info.fdLocation, entry_buf + FINDERIOFF_LOC,
229 sizeof( fh->finder_info.fdLocation ));
230 memcpy(&fh->finder_info.fdFldr, entry_buf + FINDERIOFF_FLDR,
231 sizeof( fh->finder_info.fdFldr ));
232 fh->finder_xinfo.fdScript = *(entry_buf + FINDERIOFF_SCRIPT);
233 fh->finder_xinfo.fdXFlags = *(entry_buf + FINDERIOFF_XFLAGS);
240 strncpy( type, &fh->finder_info.fdType, 4 );
241 strncpy( creator, &fh->finder_info.fdCreator, 4 );
242 type[4] = creator[4] = '\0';
243 fprintf( stderr, "type is %s, creator is %s\n", type, creator );
247 if (( single.entry[ ADEID_COMMENT ].ade_len == 0 ) ||
248 ( single.entry[ ADEID_COMMENT ].ade_off <= AD_HEADER_LEN )) {
249 fh->comment[0] = '\0';
251 pos = lseek( single.filed, single.entry[ ADEID_COMMENT ].ade_off,
253 readlen = single.entry[ ADEID_COMMENT ].ade_len > ADEDLEN_COMMENT
254 ? ADEDLEN_COMMENT : single.entry[ ADEID_COMMENT ].ade_len;
255 if ( read( single.filed, (char *)fh->comment, readlen ) != readlen ) {
256 perror( "Premature end of file :" );
261 * If date_entry is 7, we have an AppleSingle version one, do the
262 * appropriate stuff. If it is 8, we have an AppleSingle version two,
263 * do the right thing. If date_entry is neither, just use the current date.
264 * Unless I can't get the current date, in which case use time zero.
266 if (( date_entry < 7 ) || ( date_entry > 8 )) {
267 if (( time_seconds = time( NULL )) == -1 ) {
268 time_seconds = AD_DATE_START;
270 time_seconds = AD_DATE_FROM_UNIX(time_seconds);
272 memcpy(&fh->create_date, &time_seconds, sizeof( fh->create_date ));
273 memcpy(&fh->mod_date, &time_seconds, sizeof( fh->mod_date ));
274 fh->backup_date = AD_DATE_START;
275 } else if ( single.entry[ date_entry ].ade_len != 16 ) {
276 fprintf( stderr, "%s has bogus FileInfo or File Dates Info.\n",
279 } else if ( date_entry == ADEID_FILEI ) {
280 pos = lseek( single.filed,
281 single.entry[ date_entry ].ade_off, SEEK_SET );
282 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
283 sizeof( entry_buf )) {
284 perror( "Premature end of file :" );
287 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
288 sizeof( fh->create_date ));
289 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
290 sizeof( fh->mod_date ));
291 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
292 sizeof(fh->backup_date));
293 } else if ( date_entry == ADEID_FILEDATESI ) {
294 pos = lseek( single.filed,
295 single.entry[ date_entry ].ade_off, SEEK_SET );
296 if ( read( single.filed, (char *)entry_buf, sizeof( entry_buf )) !=
297 sizeof( entry_buf )) {
298 perror( "Premature end of file :" );
301 memcpy( &fh->create_date, entry_buf + FILEIOFF_CREATE,
302 sizeof( fh->create_date ));
303 memcpy( &fh->mod_date, entry_buf + FILEIOFF_MODIFY,
304 sizeof( fh->mod_date ));
305 memcpy( &fh->backup_date, entry_buf + FILEIOFF_BACKUP,
306 sizeof(fh->backup_date));
308 if ( single.entry[ ADEID_RFORK ].ade_off == 0 ) {
309 fh->forklen[ ADEID_RFORK ] = 0;
311 fh->forklen[ ADEID_RFORK ] =
312 htonl( single.entry[ ADEID_RFORK ].ade_len );
314 if ( single.entry[ ADEID_DFORK ].ade_off == 0 ) {
315 fh->forklen[ DATA ] = 0;
317 fh->forklen[ DATA ] = htonl( single.entry[ ADEID_DFORK ].ade_len );
318 pos = lseek( single.filed, single.entry[ ADEID_DFORK ].ade_off, SEEK_SET );
325 * single_header_test is called from single_open. It checks certain
326 * values of the file and determines if the file is an AppleSingle version
327 * one file something else, and returns a one, or negative one to indicate
330 * The Magic Number of the file, the first four bytes, must be hex
331 * 0x00051600. Bytes 4 through 7 are the version number and must be hex
332 * 0x00010000. Bytes 8 through 23 identify the home file system, and we
333 * are only interested in files from Macs. Therefore these bytes must
334 * contain hex 0x4d6163696e746f736820202020202020 which is ASCII
335 * "Macintosh " (that is seven blanks of padding).
337 #define MACINTOSH "Macintosh "
338 u_char sixteennulls[] = { 0, 0, 0, 0, 0, 0, 0, 0,
339 0, 0, 0, 0, 0, 0, 0, 0 };
346 cc = read( single.filed, (char *)header_buf, sizeof( header_buf ));
347 if ( cc < sizeof( header_buf )) {
348 perror( "Premature end of file :" );
352 memcpy( &templong, header_buf, sizeof( templong ));
353 if ( ntohl( templong ) != AD_APPLESINGLE_MAGIC ) {
354 fprintf( stderr, "%s is not an AppleSingle file.\n", single.path );
358 memcpy(&templong, header_buf + 4, sizeof( templong ));
359 templong = ntohl( templong );
360 if ( templong == AD_VERSION1 ) {
362 if ( memcmp( MACINTOSH, header_buf + 8, sizeof( MACINTOSH ) - 1 )
364 fprintf( stderr, "%s is not a Macintosh AppleSingle file.\n",
368 } else if ( templong == AD_VERSION2 ) {
370 if ( memcmp( sixteennulls, header_buf + 8, sizeof( sixteennulls ))
373 "Warning: %s may be a corrupt AppleSingle file.\n",
378 fprintf( stderr, "%s is a version of AppleSingle I don't understand!\n",
387 * single_read is called until it returns zero for each fork. When
388 * it returns zero for the first fork, it seeks to the proper place
389 * to read in the next, if there is one. single_read must be called
390 * enough times to return zero for each fork and no more.
394 single_read( fork, buffer, length )
407 entry_id = ADEID_DFORK;
410 entry_id = ADEID_RFORK;
417 if ( single.entry[ entry_id ].ade_len < 0 ) {
418 fprintf( stderr, "single_read: Trying to read past end of fork!\n" );
419 return( single.entry[ entry_id ].ade_len );
421 if ( single.entry[ entry_id ].ade_len == 0 ) {
422 if ( fork == DATA ) {
423 pos = lseek( single.filed,
424 single.entry[ ADEID_RFORK ].ade_off, SEEK_SET );
429 if ( single.entry[ entry_id ].ade_len < length ) {
430 readlen = single.entry[ entry_id ].ade_len;
436 while (( readlen > 0 ) && ( cc > 0 )) {
437 if (( cc = read( single.filed, buf_ptr, readlen )) > 0 ) {
443 cc = buf_ptr - buffer;
444 single.entry[ entry_id ].ade_len -= cc;