/* * CiderPress * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. * See the file LICENSE for distribution terms. */ /* * Filename manipulation, including file type preservation. This is * substantially ripped from NuLib2. */ #include "stdafx.h" #include "FileNameConv.h" #include "GenericArchive.h" #include "AddFilesDialog.h" #include #define WINDOWS_LIKE /* replace unsupported chars with '%xx' */ #define kForeignIndic '%' /* convert single hex digit char to number */ #define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \ (x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' ) /* convert number from 0-15 to hex digit */ #define HexConv(x) ( ((unsigned int)(x)) <= 15 ? \ ( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 ) /* * =========================================================================== * Common definitions * =========================================================================== */ #define kPreserveIndic '#' /* use # rather than $ for hex indication */ #define kFilenameExtDelim '.' /* separates extension from filename */ #define kResourceFlag 'r' #define kDiskImageFlag 'i' #define kMaxExtLen 5 /* ".1234" */ #define kResourceStr L"_rsrc_" /* must be longer then strlen(kResourceStr)... no problem there */ #define kMaxPathGrowth (sizeof(L"#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1) /* ProDOS file type names; must be entirely in upper case */ static const WCHAR gFileTypeNames[256][4] = { L"NON", L"BAD", L"PCD", L"PTX", L"TXT", L"PDA", L"BIN", L"FNT", L"FOT", L"BA3", L"DA3", L"WPF", L"SOS", L"$0D", L"$0E", L"DIR", L"RPD", L"RPI", L"AFD", L"AFM", L"AFR", L"SCL", L"PFS", L"$17", L"$18", L"ADB", L"AWP", L"ASP", L"$1C", L"$1D", L"$1E", L"$1F", L"TDM", L"$21", L"$22", L"$23", L"$24", L"$25", L"$26", L"$27", L"$28", L"$29", L"8SC", L"8OB", L"8IC", L"8LD", L"P8C", L"$2F", L"$30", L"$31", L"$32", L"$33", L"$34", L"$35", L"$36", L"$37", L"$38", L"$39", L"$3A", L"$3B", L"$3C", L"$3D", L"$3E", L"$3F", L"DIC", L"OCR", L"FTD", L"$43", L"$44", L"$45", L"$46", L"$47", L"$48", L"$49", L"$4A", L"$4B", L"$4C", L"$4D", L"$4E", L"$4F", L"GWP", L"GSS", L"GDB", L"DRW", L"GDP", L"HMD", L"EDU", L"STN", L"HLP", L"COM", L"CFG", L"ANM", L"MUM", L"ENT", L"DVU", L"FIN", L"$60", L"$61", L"$62", L"$63", L"$64", L"$65", L"$66", L"$67", L"$68", L"$69", L"$6A", L"BIO", L"$6C", L"TDR", L"PRE", L"HDV", L"$70", L"$71", L"$72", L"$73", L"$74", L"$75", L"$76", L"$77", L"$78", L"$79", L"$7A", L"$7B", L"$7C", L"$7D", L"$7E", L"$7F", L"$80", L"$81", L"$82", L"$83", L"$84", L"$85", L"$86", L"$87", L"$88", L"$89", L"$8A", L"$8B", L"$8C", L"$8D", L"$8E", L"$8F", L"$90", L"$91", L"$92", L"$93", L"$94", L"$95", L"$96", L"$97", L"$98", L"$99", L"$9A", L"$9B", L"$9C", L"$9D", L"$9E", L"$9F", L"WP ", L"$A1", L"$A2", L"$A3", L"$A4", L"$A5", L"$A6", L"$A7", L"$A8", L"$A9", L"$AA", L"GSB", L"TDF", L"BDF", L"$AE", L"$AF", L"SRC", L"OBJ", L"LIB", L"S16", L"RTL", L"EXE", L"PIF", L"TIF", L"NDA", L"CDA", L"TOL", L"DVR", L"LDF", L"FST", L"$BE", L"DOC", L"PNT", L"PIC", L"ANI", L"PAL", L"$C4", L"OOG", L"SCR", L"CDV", L"FON", L"FND", L"ICN", L"$CB", L"$CC", L"$CD", L"$CE", L"$CF", L"$D0", L"$D1", L"$D2", L"$D3", L"$D4", L"MUS", L"INS", L"MDI", L"SND", L"$D9", L"$DA", L"DBM", L"$DC", L"DDD", L"$DE", L"$DF", L"LBR", L"$E1", L"ATK", L"$E3", L"$E4", L"$E5", L"$E6", L"$E7", L"$E8", L"$E9", L"$EA", L"$EB", L"$EC", L"$ED", L"R16", L"PAS", L"CMD", L"$F1", L"$F2", L"$F3", L"$F4", L"$F5", L"$F6", L"$F7", L"$F8", L"OS ", L"INT", L"IVR", L"BAS", L"VAR", L"REL", L"SYS" }; /* * Some file extensions we recognize. When adding files with "extended" * preservation mode, we try to assign types to files that weren't * explicitly preserved, but nevertheless have a recognizeable type. * * geoff at gwlink.net pointed out that this really ought to be in an external * file rather than a hard-coded table. Ought to fix that someday. */ static const struct { const WCHAR* label; unsigned short fileType; unsigned long auxType; unsigned char flags; } gRecognizedExtensions[] = { { L"ASM", 0xb0, 0x0003, 0 }, /* APW assembly source */ { L"C", 0xb0, 0x000a, 0 }, /* APW C source */ { L"H", 0xb0, 0x000a, 0 }, /* APW C header */ { L"CPP", 0xb0, 0x0000, 0 }, /* generic source file */ { L"BNY", 0xe0, 0x8000, 0 }, /* Binary II lib */ { L"BQY", 0xe0, 0x8000, 0 }, /* Binary II lib, w/ compress */ { L"BXY", 0xe0, 0x8000, 0 }, /* Binary II wrap around SHK */ { L"BSE", 0xe0, 0x8000, 0 }, /* Binary II wrap around SEA */ { L"SEA", 0xb3, 0xdb07, 0 }, /* GSHK SEA */ { L"TEXT", 0x04, 0x0000, 0 }, /* ASCII Text */ { L"GIF", 0xc0, 0x8006, 0 }, /* GIF image */ { L"JPG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ { L"JPEG", 0x06, 0x0000, 0 }, /* JPEG (nicer than 'NON') */ //{ L"ACU", 0xe0, 0x8001, 0 }, /* ACU archive */ { L"SHK", 0xe0, 0x8002, 0 }, /* ShrinkIt archive */ }; /* * Return a pointer to the three-letter representation of the file type name. * * Note to self: code down below tests first char for '?'. */ /*static*/ const WCHAR* PathProposal::FileTypeString(unsigned long fileType) { if (fileType < NELEM(gFileTypeNames)) return gFileTypeNames[fileType]; else return kUnknownTypeStr; } /* * Description table. * * The first item that matches will be used, but the table is searched * bottom-up, so it's important to have the most general entry first. * * In retrospect, it might have made sense to use the same format as the * "FTD" file type description file that the IIgs Finder used. Might have * made sense to just ship that and load it on startup (although copyright * issues would have to be investigated). */ static const struct { unsigned short fileType; unsigned short minAuxType; // start of range for which this applies unsigned short maxAuxType; // end of range const WCHAR* descr; } gTypeDescriptions[] = { /*NON*/ { 0x00, 0x0000, 0xffff, L"Untyped file" }, /*BAD*/ { 0x01, 0x0000, 0xffff, L"Bad blocks" }, /*PCD*/ { 0x02, 0x0000, 0xffff, L"Pascal code" }, /*PTX*/ { 0x03, 0x0000, 0xffff, L"Pascal text" }, /*TXT*/ { 0x04, 0x0000, 0xffff, L"ASCII text" }, /*PDA*/ { 0x05, 0x0000, 0xffff, L"Pascal data" }, /*BIN*/ { 0x06, 0x0000, 0xffff, L"Binary" }, /*FNT*/ { 0x07, 0x0000, 0xffff, L"Apple /// font" }, /*FOT*/ { 0x08, 0x0000, 0xffff, L"Apple II or /// graphics" }, /* */ { 0x08, 0x0000, 0x3fff, L"Apple II graphics" }, /* */ { 0x08, 0x4000, 0x4000, L"Packed hi-res image" }, /* */ { 0x08, 0x4001, 0x4001, L"Packed double hi-res image" }, /* */ { 0x08, 0x8001, 0x8001, L"Printographer packed HGR file" }, /* */ { 0x08, 0x8002, 0x8002, L"Printographer packed DHGR file" }, /* */ { 0x08, 0x8003, 0x8003, L"Softdisk hi-res image" }, /* */ { 0x08, 0x8004, 0x8004, L"Softdisk double hi-res image" }, /*BA3*/ { 0x09, 0x0000, 0xffff, L"Apple /// BASIC program" }, /*DA3*/ { 0x0a, 0x0000, 0xffff, L"Apple /// BASIC data" }, /*WPF*/ { 0x0b, 0x0000, 0xffff, L"Apple II or /// word processor" }, /* */ { 0x0b, 0x8001, 0x8001, L"Write This Way document" }, /* */ { 0x0b, 0x8002, 0x8002, L"Writing & Publishing document" }, /*SOS*/ { 0x0c, 0x0000, 0xffff, L"Apple /// SOS system" }, /*DIR*/ { 0x0f, 0x0000, 0xffff, L"Folder" }, /*RPD*/ { 0x10, 0x0000, 0xffff, L"Apple /// RPS data" }, /*RPI*/ { 0x11, 0x0000, 0xffff, L"Apple /// RPS index" }, /*AFD*/ { 0x12, 0x0000, 0xffff, L"Apple /// AppleFile discard" }, /*AFM*/ { 0x13, 0x0000, 0xffff, L"Apple /// AppleFile model" }, /*AFR*/ { 0x14, 0x0000, 0xffff, L"Apple /// AppleFile report format" }, /*SCL*/ { 0x15, 0x0000, 0xffff, L"Apple /// screen library" }, /*PFS*/ { 0x16, 0x0000, 0xffff, L"PFS document" }, /* */ { 0x16, 0x0001, 0x0001, L"PFS:File document" }, /* */ { 0x16, 0x0002, 0x0002, L"PFS:Write document" }, /* */ { 0x16, 0x0003, 0x0003, L"PFS:Graph document" }, /* */ { 0x16, 0x0004, 0x0004, L"PFS:Plan document" }, /* */ { 0x16, 0x0016, 0x0016, L"PFS internal data" }, /*ADB*/ { 0x19, 0x0000, 0xffff, L"AppleWorks data base" }, /*AWP*/ { 0x1a, 0x0000, 0xffff, L"AppleWorks word processor" }, /*ASP*/ { 0x1b, 0x0000, 0xffff, L"AppleWorks spreadsheet" }, /*TDM*/ { 0x20, 0x0000, 0xffff, L"Desktop Manager document" }, /*???*/ { 0x21, 0x0000, 0xffff, L"Instant Pascal source" }, /*???*/ { 0x22, 0x0000, 0xffff, L"UCSD Pascal volume" }, /*???*/ { 0x29, 0x0000, 0xffff, L"Apple /// SOS dictionary" }, /*8SC*/ { 0x2a, 0x0000, 0xffff, L"Apple II source code" }, /* */ { 0x2a, 0x8001, 0x8001, L"EBBS command script" }, /*8OB*/ { 0x2b, 0x0000, 0xffff, L"Apple II object code" }, /* */ { 0x2b, 0x8001, 0x8001, L"GBBS Pro object Code" }, /*8IC*/ { 0x2c, 0x0000, 0xffff, L"Apple II interpreted code" }, /* */ { 0x2c, 0x8003, 0x8003, L"APEX Program File" }, /* */ { 0x2c, 0x8005, 0x8005, L"EBBS tokenized command script" }, /*8LD*/ { 0x2d, 0x0000, 0xffff, L"Apple II language data" }, /* */ { 0x2d, 0x8006, 0x8005, L"EBBS message bundle" }, /* */ { 0x2d, 0x8007, 0x8007, L"EBBS compressed message bundle" }, /*P8C*/ { 0x2e, 0x0000, 0xffff, L"ProDOS 8 code module" }, /* */ { 0x2e, 0x8001, 0x8001, L"Davex 8 Command" }, /*PTP*/ { 0x2e, 0x8002, 0x8002, L"Point-to-Point drivers" }, /*PTP*/ { 0x2e, 0x8003, 0x8003, L"Point-to-Point code" }, /* */ { 0x2e, 0x8004, 0x8004, L"Softdisk printer driver" }, /*DIC*/ { 0x40, 0x0000, 0xffff, L"Dictionary file" }, /*???*/ { 0x41, 0x0000, 0xffff, L"OCR data" }, /* */ { 0x41, 0x8001, 0x8001, L"InWords OCR font table" }, /*FTD*/ { 0x42, 0x0000, 0xffff, L"File type names" }, /*???*/ { 0x43, 0x0000, 0xffff, L"Peripheral data" }, /* */ { 0x43, 0x8001, 0x8001, L"Express document" }, /*???*/ { 0x44, 0x0000, 0xffff, L"Personal information" }, /* */ { 0x44, 0x8001, 0x8001, L"ResuMaker personal information" }, /* */ { 0x44, 0x8002, 0x8002, L"ResuMaker resume" }, /* */ { 0x44, 0x8003, 0x8003, L"II Notes document" }, /* */ { 0x44, 0x8004, 0x8004, L"Softdisk scrapbook document" }, /* */ { 0x44, 0x8005, 0x8005, L"Don't Forget document" }, /* */ { 0x44, 0x80ff, 0x80ff, L"What To Do data" }, /* */ { 0x44, 0xbeef, 0xbeef, L"Table Scraps scrapbook" }, /*???*/ { 0x45, 0x0000, 0xffff, L"Mathematical document" }, /* */ { 0x45, 0x8001, 0x8001, L"GSymbolix 3D graph document" }, /* */ { 0x45, 0x8002, 0x8002, L"GSymbolix formula document" }, /*???*/ { 0x46, 0x0000, 0xffff, L"AutoSave profiles" }, /* */ { 0x46, 0x8001, 0x8001, L"AutoSave profiles" }, /*GWP*/ { 0x50, 0x0000, 0xffff, L"Apple IIgs Word Processor" }, /* */ { 0x50, 0x8001, 0x8001, L"DeluxeWrite document" }, /* */ { 0x50, 0x8003, 0x8003, L"Personal Journal document" }, /* */ { 0x50, 0x8010, 0x8010, L"AppleWorks GS word processor" }, /* */ { 0x50, 0x8011, 0x8011, L"Softdisk issue text" }, /* */ { 0x50, 0x5445, 0x5445, L"Teach document" }, /*GSS*/ { 0x51, 0x0000, 0xffff, L"Apple IIgs spreadsheet" }, /* */ { 0x51, 0x8010, 0x8010, L"AppleWorks GS spreadsheet" }, /* */ { 0x51, 0x2358, 0x2358, L"QC Calc spreadsheet " }, /*GDB*/ { 0x52, 0x0000, 0xffff, L"Apple IIgs data base" }, /* */ { 0x52, 0x8001, 0x8001, L"GTv database" }, /* */ { 0x52, 0x8010, 0x8010, L"AppleWorks GS data base" }, /* */ { 0x52, 0x8011, 0x8011, L"AppleWorks GS DB template" }, /* */ { 0x52, 0x8013, 0x8013, L"GSAS database" }, /* */ { 0x52, 0x8014, 0x8014, L"GSAS accounting journals" }, /* */ { 0x52, 0x8015, 0x8015, L"Address Manager document" }, /* */ { 0x52, 0x8016, 0x8016, L"Address Manager defaults" }, /* */ { 0x52, 0x8017, 0x8017, L"Address Manager index" }, /*DRW*/ { 0x53, 0x0000, 0xffff, L"Drawing" }, /* */ { 0x53, 0x8002, 0x8002, L"Graphic Disk Labeler document" }, /* */ { 0x53, 0x8010, 0x8010, L"AppleWorks GS graphics" }, /*GDP*/ { 0x54, 0x0000, 0xffff, L"Desktop publishing" }, /* */ { 0x54, 0x8002, 0x8002, L"GraphicWriter document" }, /* */ { 0x54, 0x8003, 0x8003, L"Label It document" }, /* */ { 0x54, 0x8010, 0x8010, L"AppleWorks GS Page Layout" }, /* */ { 0x54, 0xdd3e, 0xdd3e, L"Medley document" }, /*HMD*/ { 0x55, 0x0000, 0xffff, L"Hypermedia" }, /* */ { 0x55, 0x0001, 0x0001, L"HyperCard IIgs stack" }, /* */ { 0x55, 0x8001, 0x8001, L"Tutor-Tech document" }, /* */ { 0x55, 0x8002, 0x8002, L"HyperStudio document" }, /* */ { 0x55, 0x8003, 0x8003, L"Nexus document" }, /* */ { 0x55, 0x8004, 0x8004, L"HyperSoft stack" }, /* */ { 0x55, 0x8005, 0x8005, L"HyperSoft card" }, /* */ { 0x55, 0x8006, 0x8006, L"HyperSoft external command" }, /*EDU*/ { 0x56, 0x0000, 0xffff, L"Educational Data" }, /* */ { 0x56, 0x8001, 0x8001, L"Tutor-Tech scores" }, /* */ { 0x56, 0x8007, 0x8007, L"GradeBook data" }, /*STN*/ { 0x57, 0x0000, 0xffff, L"Stationery" }, /* */ { 0x57, 0x8003, 0x8003, L"Music Writer format" }, /*HLP*/ { 0x58, 0x0000, 0xffff, L"Help file" }, /* */ { 0x58, 0x8002, 0x8002, L"Davex 8 help file" }, /* */ { 0x58, 0x8005, 0x8005, L"Micol Advanced Basic help file" }, /* */ { 0x58, 0x8006, 0x8006, L"Locator help document" }, /* */ { 0x58, 0x8007, 0x8007, L"Personal Journal help" }, /* */ { 0x58, 0x8008, 0x8008, L"Home Refinancer help" }, /* */ { 0x58, 0x8009, 0x8009, L"The Optimizer help" }, /* */ { 0x58, 0x800a, 0x800a, L"Text Wizard help" }, /* */ { 0x58, 0x800b, 0x800b, L"WordWorks Pro help system" }, /* */ { 0x58, 0x800c, 0x800c, L"Sound Wizard help" }, /* */ { 0x58, 0x800d, 0x800d, L"SeeHear help system" }, /* */ { 0x58, 0x800e, 0x800e, L"QuickForms help system" }, /* */ { 0x58, 0x800f, 0x800f, L"Don't Forget help system" }, /*COM*/ { 0x59, 0x0000, 0xffff, L"Communications file" }, /* */ { 0x59, 0x8002, 0x8002, L"AppleWorks GS communications" }, /*CFG*/ { 0x5a, 0x0000, 0xffff, L"Configuration file" }, /* */ { 0x5a, 0x0000, 0x0000, L"Sound settings files" }, /* */ { 0x5a, 0x0002, 0x0002, L"Battery RAM configuration" }, /* */ { 0x5a, 0x0003, 0x0003, L"AutoLaunch preferences" }, /* */ { 0x5a, 0x0004, 0x0004, L"SetStart preferences" }, /* */ { 0x5a, 0x0005, 0x0005, L"GSBug configuration" }, /* */ { 0x5a, 0x0006, 0x0006, L"Archiver preferences" }, /* */ { 0x5a, 0x0007, 0x0007, L"Archiver table of contents" }, /* */ { 0x5a, 0x0008, 0x0008, L"Font Manager data" }, /* */ { 0x5a, 0x0009, 0x0009, L"Print Manager data" }, /* */ { 0x5a, 0x000a, 0x000a, L"IR preferences" }, /* */ { 0x5a, 0x8001, 0x8001, L"Master Tracks Jr. preferences" }, /* */ { 0x5a, 0x8002, 0x8002, L"GraphicWriter preferences" }, /* */ { 0x5a, 0x8003, 0x8003, L"Z-Link configuration" }, /* */ { 0x5a, 0x8004, 0x8004, L"JumpStart configuration" }, /* */ { 0x5a, 0x8005, 0x8005, L"Davex 8 configuration" }, /* */ { 0x5a, 0x8006, 0x8006, L"Nifty List configuration" }, /* */ { 0x5a, 0x8007, 0x8007, L"GTv videodisc configuration" }, /* */ { 0x5a, 0x8008, 0x8008, L"GTv Workshop configuration" }, /*PTP*/ { 0x5a, 0x8009, 0x8009, L"Point-to-Point preferences" }, /* */ { 0x5a, 0x800a, 0x800a, L"ORCA/Disassembler preferences" }, /* */ { 0x5a, 0x800b, 0x800b, L"SnowTerm preferences" }, /* */ { 0x5a, 0x800c, 0x800c, L"My Word! preferences" }, /* */ { 0x5a, 0x800d, 0x800d, L"Chipmunk configuration" }, /* */ { 0x5a, 0x8010, 0x8010, L"AppleWorks GS configuration" }, /* */ { 0x5a, 0x8011, 0x8011, L"SDE Shell preferences" }, /* */ { 0x5a, 0x8012, 0x8012, L"SDE Editor preferences" }, /* */ { 0x5a, 0x8013, 0x8013, L"SDE system tab ruler" }, /* */ { 0x5a, 0x8014, 0x8014, L"Nexus configuration" }, /* */ { 0x5a, 0x8015, 0x8015, L"DesignMaster preferences" }, /* */ { 0x5a, 0x801a, 0x801a, L"MAX/Edit keyboard template" }, /* */ { 0x5a, 0x801b, 0x801b, L"MAX/Edit tab ruler set" }, /* */ { 0x5a, 0x801c, 0x801c, L"Platinum Paint preferences" }, /* */ { 0x5a, 0x801d, 0x801d, L"Sea Scan 1000" }, /* */ { 0x5a, 0x801e, 0x801e, L"Allison preferences" }, /* */ { 0x5a, 0x801f, 0x801f, L"Gold of the Americas options" }, /* */ { 0x5a, 0x8021, 0x8021, L"GSAS accounting setup" }, /* */ { 0x5a, 0x8022, 0x8022, L"GSAS accounting document" }, /* */ { 0x5a, 0x8023, 0x8023, L"UtilityLaunch preferences" }, /* */ { 0x5a, 0x8024, 0x8024, L"Softdisk configuration" }, /* */ { 0x5a, 0x8025, 0x8025, L"Quit-To configuration" }, /* */ { 0x5a, 0x8026, 0x8026, L"Big Edit Thing" }, /* */ { 0x5a, 0x8027, 0x8027, L"ZMaker preferences" }, /* */ { 0x5a, 0x8028, 0x8028, L"Minstrel configuration" }, /* */ { 0x5a, 0x8029, 0x8029, L"WordWorks Pro preferences" }, /* */ { 0x5a, 0x802b, 0x802b, L"Pointless preferences" }, /* */ { 0x5a, 0x802c, 0x802c, L"Micol Advanced Basic config" }, /* */ { 0x5a, 0x802e, 0x802e, L"Label It configuration" }, /* */ { 0x5a, 0x802f, 0x802f, L"Cool Cursor document" }, /* */ { 0x5a, 0x8030, 0x8030, L"Locator preferences" }, /* */ { 0x5a, 0x8031, 0x8031, L"Replicator preferences" }, /* */ { 0x5a, 0x8032, 0x8032, L"Kangaroo configuration" }, /* */ { 0x5a, 0x8033, 0x8033, L"Kangaroo data" }, /* */ { 0x5a, 0x8034, 0x8034, L"TransProg III configuration" }, /* */ { 0x5a, 0x8035, 0x8035, L"Home Refinancer preferences" }, /* */ { 0x5a, 0x8036, 0x8036, L"Easy Eyes settings" }, /* */ { 0x5a, 0x8037, 0x8037, L"The Optimizer settings" }, /* */ { 0x5a, 0x8038, 0x8038, L"Text Wizard settings" }, /* */ { 0x5a, 0x803b, 0x803b, L"Disk Access II preferences" }, /* */ { 0x5a, 0x803d, 0x803d, L"Quick DA configuration" }, /* */ { 0x5a, 0x803e, 0x803e, L"Crazy 8s preferences" }, /* */ { 0x5a, 0x803f, 0x803f, L"Sound Wizard settings" }, /* */ { 0x5a, 0x8041, 0x8041, L"Quick Window configuration" }, /* */ { 0x5a, 0x8044, 0x8044, L"Universe Master disk map" }, /* */ { 0x5a, 0x8046, 0x8046, L"Autopilot configuration" }, /* */ { 0x5a, 0x8047, 0x8047, L"EGOed preferences" }, /* */ { 0x5a, 0x8049, 0x8049, L"Quick DA preferences" }, /* */ { 0x5a, 0x804b, 0x804b, L"HardPressed volume preferences" }, /* */ { 0x5a, 0x804c, 0x804c, L"HardPressed global preferences" }, /* */ { 0x5a, 0x804d, 0x804d, L"HardPressed profile" }, /* */ { 0x5a, 0x8050, 0x8050, L"Don't Forget settings" }, /* */ { 0x5a, 0x8052, 0x8052, L"ProBOOT preferences" }, /* */ { 0x5a, 0x8054, 0x8054, L"Battery Brain preferences" }, /* */ { 0x5a, 0x8055, 0x8055, L"Rainbow configuration" }, /* */ { 0x5a, 0x8061, 0x8061, L"TypeSet preferences" }, /* */ { 0x5a, 0x8063, 0x8063, L"Cool Cursor preferences" }, /* */ { 0x5a, 0x806e, 0x806e, L"Balloon preferences" }, /* */ { 0x5a, 0x80fe, 0x80fe, L"Special Edition configuration" }, /* */ { 0x5a, 0x80ff, 0x80ff, L"Sun Dial preferences" }, /*ANM*/ { 0x5b, 0x0000, 0xffff, L"Animation file" }, /* */ { 0x5b, 0x8001, 0x8001, L"Cartooners movie" }, /* */ { 0x5b, 0x8002, 0x8002, L"Cartooners actors" }, /* */ { 0x5b, 0x8005, 0x8005, L"Arcade King Super document" }, /* */ { 0x5b, 0x8006, 0x8006, L"Arcade King DHRG document" }, /* */ { 0x5b, 0x8007, 0x8007, L"DreamVision movie" }, /*MUM*/ { 0x5c, 0x0000, 0xffff, L"Multimedia document" }, /* */ { 0x5c, 0x8001, 0x8001, L"GTv multimedia playlist" }, /*ENT*/ { 0x5d, 0x0000, 0xffff, L"Game/Entertainment document" }, /* */ { 0x5d, 0x8001, 0x8001, L"Solitaire Royale document" }, /* */ { 0x5d, 0x8002, 0x8002, L"BattleFront scenario" }, /* */ { 0x5d, 0x8003, 0x8003, L"BattleFront saved game" }, /* */ { 0x5d, 0x8004, 0x8004, L"Gold of the Americas game" }, /* */ { 0x5d, 0x8006, 0x8006, L"Blackjack Tutor document" }, /* */ { 0x5d, 0x8008, 0x8008, L"Canasta document" }, /* */ { 0x5d, 0x800b, 0x800b, L"Word Search document" }, /* */ { 0x5d, 0x800c, 0x800c, L"Tarot deal" }, /* */ { 0x5d, 0x800d, 0x800d, L"Tarot tournament" }, /* */ { 0x5d, 0x800e, 0x800e, L"Full Metal Planet game" }, /* */ { 0x5d, 0x800f, 0x800f, L"Full Metal Planet player" }, /* */ { 0x5d, 0x8010, 0x8010, L"Quizzical high scores" }, /* */ { 0x5d, 0x8011, 0x8011, L"Meltdown high scores" }, /* */ { 0x5d, 0x8012, 0x8012, L"BlockWords high scores" }, /* */ { 0x5d, 0x8013, 0x8013, L"Lift-A-Gon scores" }, /* */ { 0x5d, 0x8014, 0x8014, L"Softdisk Adventure" }, /* */ { 0x5d, 0x8015, 0x8015, L"Blankety Blank document" }, /* */ { 0x5d, 0x8016, 0x8016, L"Son of Star Axe champion" }, /* */ { 0x5d, 0x8017, 0x8017, L"Digit Fidget high scores" }, /* */ { 0x5d, 0x8018, 0x8018, L"Eddie map" }, /* */ { 0x5d, 0x8019, 0x8019, L"Eddie tile set" }, /* */ { 0x5d, 0x8122, 0x8122, L"Wolfenstein 3D scenario" }, /* */ { 0x5d, 0x8123, 0x8123, L"Wolfenstein 3D saved game" }, /*DVU*/ { 0x5e, 0x0000, 0xffff, L"Development utility document" }, /* */ { 0x5e, 0x0001, 0x0001, L"Resource file" }, /* */ { 0x5e, 0x8001, 0x8001, L"ORCA/Disassembler template" }, /* */ { 0x5e, 0x8003, 0x8003, L"DesignMaster document" }, /* */ { 0x5e, 0x8008, 0x8008, L"ORCA/C symbol file" }, /*FIN*/ { 0x5f, 0x0000, 0xffff, L"Financial document" }, /* */ { 0x5f, 0x8001, 0x8001, L"Your Money Matters document" }, /* */ { 0x5f, 0x8002, 0x8002, L"Home Refinancer document" }, /*BIO*/ { 0x6b, 0x0000, 0xffff, L"PC Transporter BIOS" }, /*TDR*/ { 0x6d, 0x0000, 0xffff, L"PC Transporter driver" }, /*PRE*/ { 0x6e, 0x0000, 0xffff, L"PC Transporter pre-boot" }, /*HDV*/ { 0x6f, 0x0000, 0xffff, L"PC Transporter volume" }, /*WP */ { 0xa0, 0x0000, 0xffff, L"WordPerfect document" }, /*GSB*/ { 0xab, 0x0000, 0xffff, L"Apple IIgs BASIC program" }, /*TDF*/ { 0xac, 0x0000, 0xffff, L"Apple IIgs BASIC TDF" }, /*BDF*/ { 0xad, 0x0000, 0xffff, L"Apple IIgs BASIC data" }, /*SRC*/ { 0xb0, 0x0000, 0xffff, L"Apple IIgs source code" }, /* */ { 0xb0, 0x0001, 0x0001, L"APW Text file" }, /* */ { 0xb0, 0x0003, 0x0003, L"APW 65816 Assembly source code" }, /* */ { 0xb0, 0x0005, 0x0005, L"ORCA/Pascal source code" }, /* */ { 0xb0, 0x0006, 0x0006, L"APW command file" }, /* */ { 0xb0, 0x0008, 0x0008, L"ORCA/C source code" }, /* */ { 0xb0, 0x0009, 0x0009, L"APW Linker command file" }, /* */ { 0xb0, 0x000a, 0x000a, L"APW C source code" }, /* */ { 0xb0, 0x000c, 0x000c, L"ORCA/Desktop command file" }, /* */ { 0xb0, 0x0015, 0x0015, L"APW Rez source file" }, /* */ { 0xb0, 0x0017, 0x0017, L"Installer script" }, /* */ { 0xb0, 0x001e, 0x001e, L"TML Pascal source code" }, /* */ { 0xb0, 0x0116, 0x0116, L"ORCA/Disassembler script" }, /* */ { 0xb0, 0x0503, 0x0503, L"SDE Assembler source code" }, /* */ { 0xb0, 0x0506, 0x0506, L"SDE command script" }, /* */ { 0xb0, 0x0601, 0x0601, L"Nifty List data" }, /* */ { 0xb0, 0x0719, 0x0719, L"PostScript file" }, /*OBJ*/ { 0xb1, 0x0000, 0xffff, L"Apple IIgs object code" }, /*LIB*/ { 0xb2, 0x0000, 0xffff, L"Apple IIgs Library file" }, /*S16*/ { 0xb3, 0x0000, 0xffff, L"GS/OS application" }, /*RTL*/ { 0xb4, 0x0000, 0xffff, L"GS/OS run-time library" }, /*EXE*/ { 0xb5, 0x0000, 0xffff, L"GS/OS shell application" }, /*PIF*/ { 0xb6, 0x0000, 0xffff, L"Permanent initialization file" }, /*TIF*/ { 0xb7, 0x0000, 0xffff, L"Temporary initialization file" }, /*NDA*/ { 0xb8, 0x0000, 0xffff, L"New desk accessory" }, /*CDA*/ { 0xb9, 0x0000, 0xffff, L"Classic desk accessory" }, /*TOL*/ { 0xba, 0x0000, 0xffff, L"Tool" }, /*DVR*/ { 0xbb, 0x0000, 0xffff, L"Apple IIgs device driver file" }, /* */ { 0xbb, 0x7e01, 0x7e01, L"GNO/ME terminal device driver" }, /* */ { 0xbb, 0x7f01, 0x7f01, L"GTv videodisc serial driver" }, /* */ { 0xbb, 0x7f02, 0x7f02, L"GTv videodisc game port driver" }, /*LDF*/ { 0xbc, 0x0000, 0xffff, L"Load file (generic)" }, /* */ { 0xbc, 0x4001, 0x4001, L"Nifty List module" }, /* */ { 0xbc, 0xc001, 0xc001, L"Nifty List module" }, /* */ { 0xbc, 0x4002, 0x4002, L"Super Info module" }, /* */ { 0xbc, 0xc002, 0xc002, L"Super Info module" }, /* */ { 0xbc, 0x4004, 0x4004, L"Twilight document" }, /* */ { 0xbc, 0xc004, 0xc004, L"Twilight document" }, /* */ { 0xbc, 0x4006, 0x4006, L"Foundation resource editor" }, /* */ { 0xbc, 0xc006, 0xc006, L"Foundation resource editor" }, /* */ { 0xbc, 0x4007, 0x4007, L"HyperStudio new button action" }, /* */ { 0xbc, 0xc007, 0xc007, L"HyperStudio new button action" }, /* */ { 0xbc, 0x4008, 0x4008, L"HyperStudio screen transition" }, /* */ { 0xbc, 0xc008, 0xc008, L"HyperStudio screen transition" }, /* */ { 0xbc, 0x4009, 0x4009, L"DreamGrafix module" }, /* */ { 0xbc, 0xc009, 0xc009, L"DreamGrafix module" }, /* */ { 0xbc, 0x400a, 0x400a, L"HyperStudio Extra utility" }, /* */ { 0xbc, 0xc00a, 0xc00a, L"HyperStudio Extra utility" }, /* */ { 0xbc, 0x400f, 0x400f, L"HardPressed module" }, /* */ { 0xbc, 0xc00f, 0xc00f, L"HardPressed module" }, /* */ { 0xbc, 0x4010, 0x4010, L"Graphic Exchange translator" }, /* */ { 0xbc, 0xc010, 0xc010, L"Graphic Exchange translator" }, /* */ { 0xbc, 0x4011, 0x4011, L"Desktop Enhancer blanker" }, /* */ { 0xbc, 0xc011, 0xc011, L"Desktop Enhancer blanker" }, /* */ { 0xbc, 0x4083, 0x4083, L"Marinetti link layer module" }, /* */ { 0xbc, 0xc083, 0xc083, L"Marinetti link layer module" }, /*FST*/ { 0xbd, 0x0000, 0xffff, L"GS/OS File System Translator" }, /*DOC*/ { 0xbf, 0x0000, 0xffff, L"GS/OS document" }, /*PNT*/ { 0xc0, 0x0000, 0xffff, L"Packed super hi-res picture" }, /* */ { 0xc0, 0x0000, 0x0000, L"Paintworks packed picture" }, /* */ { 0xc0, 0x0001, 0x0001, L"Packed super hi-res image" }, /* */ { 0xc0, 0x0002, 0x0002, L"Apple Preferred Format picture" }, /* */ { 0xc0, 0x0003, 0x0003, L"Packed QuickDraw II PICT file" }, /* */ { 0xc0, 0x0080, 0x0080, L"TIFF document" }, /* */ { 0xc0, 0x0081, 0x0081, L"JFIF (JPEG) document" }, /* */ { 0xc0, 0x8001, 0x8001, L"GTv background image" }, /* */ { 0xc0, 0x8005, 0x8005, L"DreamGrafix document" }, /* */ { 0xc0, 0x8006, 0x8006, L"GIF document" }, /*PIC*/ { 0xc1, 0x0000, 0xffff, L"Super hi-res picture" }, /* */ { 0xc1, 0x0000, 0x0000, L"Super hi-res screen image" }, /* */ { 0xc1, 0x0001, 0x0001, L"QuickDraw PICT file" }, /* */ { 0xc1, 0x0002, 0x0002, L"Super hi-res 3200-color screen image" }, /* */ { 0xc1, 0x8001, 0x8001, L"Allison raw image doc" }, /* */ { 0xc1, 0x8002, 0x8002, L"ThunderScan image doc" }, /* */ { 0xc1, 0x8003, 0x8003, L"DreamGrafix document" }, /*ANI*/ { 0xc2, 0x0000, 0xffff, L"Paintworks animation" }, /*PAL*/ { 0xc3, 0x0000, 0xffff, L"Paintworks palette" }, /*OOG*/ { 0xc5, 0x0000, 0xffff, L"Object-oriented graphics" }, /* */ { 0xc5, 0x8000, 0x8000, L"Draw Plus document" }, /* */ { 0xc5, 0xc000, 0xc000, L"DYOH architecture doc" }, /* */ { 0xc5, 0xc001, 0xc001, L"DYOH predrawn objects" }, /* */ { 0xc5, 0xc002, 0xc002, L"DYOH custom objects" }, /* */ { 0xc5, 0xc003, 0xc003, L"DYOH clipboard" }, /* */ { 0xc5, 0xc004, 0xc004, L"DYOH interiors document" }, /* */ { 0xc5, 0xc005, 0xc005, L"DYOH patterns" }, /* */ { 0xc5, 0xc006, 0xc006, L"DYOH landscape document" }, /* */ { 0xc5, 0xc007, 0xc007, L"PyWare Document" }, /*SCR*/ { 0xc6, 0x0000, 0xffff, L"Script" }, /* */ { 0xc6, 0x8001, 0x8001, L"Davex 8 script" }, /* */ { 0xc6, 0x8002, 0x8002, L"Universe Master backup script" }, /* */ { 0xc6, 0x8003, 0x8003, L"Universe Master Chain script" }, /*CDV*/ { 0xc7, 0x0000, 0xffff, L"Control Panel document" }, /*FON*/ { 0xc8, 0x0000, 0xffff, L"Font" }, /* */ { 0xc8, 0x0000, 0x0000, L"Font (Standard Apple IIgs QuickDraw II Font)" }, /* */ { 0xc8, 0x0001, 0x0001, L"TrueType font resource" }, /* */ { 0xc8, 0x0008, 0x0008, L"Postscript font resource" }, /* */ { 0xc8, 0x0081, 0x0081, L"TrueType font file" }, /* */ { 0xc8, 0x0088, 0x0088, L"Postscript font file" }, /*FND*/ { 0xc9, 0x0000, 0xffff, L"Finder data" }, /*ICN*/ { 0xca, 0x0000, 0xffff, L"Icons" }, /*MUS*/ { 0xd5, 0x0000, 0xffff, L"Music sequence" }, /* */ { 0xd5, 0x0000, 0x0000, L"Music Construction Set song" }, /* */ { 0xd5, 0x0001, 0x0001, L"MIDI Synth sequence" }, /* */ { 0xd5, 0x0007, 0x0007, L"SoundSmith document" }, /* */ { 0xd5, 0x8002, 0x8002, L"Diversi-Tune sequence" }, /* */ { 0xd5, 0x8003, 0x8003, L"Master Tracks Jr. sequence" }, /* */ { 0xd5, 0x8004, 0x8004, L"Music Writer document" }, /* */ { 0xd5, 0x8005, 0x8005, L"Arcade King Super music" }, /* */ { 0xd5, 0x8006, 0x8006, L"Music Composer file" }, /*INS*/ { 0xd6, 0x0000, 0xffff, L"Instrument" }, /* */ { 0xd6, 0x0000, 0x0000, L"Music Construction Set instrument" }, /* */ { 0xd6, 0x0001, 0x0001, L"MIDI Synth instrument" }, /* */ { 0xd6, 0x8002, 0x8002, L"Diversi-Tune instrument" }, /*MDI*/ { 0xd7, 0x0000, 0xffff, L"MIDI data" }, /* */ { 0xd7, 0x0000, 0x0000, L"MIDI standard data" }, /* */ { 0xd7, 0x0080, 0x0080, L"MIDI System Exclusive data" }, /* */ { 0xd7, 0x8001, 0x8001, L"MasterTracks Pro Sysex file" }, /*SND*/ { 0xd8, 0x0000, 0xffff, L"Sampled sound" }, /* */ { 0xd8, 0x0000, 0x0000, L"Audio IFF document" }, /* */ { 0xd8, 0x0001, 0x0001, L"AIFF-C document" }, /* */ { 0xd8, 0x0002, 0x0002, L"ASIF instrument" }, /* */ { 0xd8, 0x0003, 0x0003, L"Sound resource file" }, /* */ { 0xd8, 0x0004, 0x0004, L"MIDI Synth wave data" }, /* */ { 0xd8, 0x8001, 0x8001, L"HyperStudio sound" }, /* */ { 0xd8, 0x8002, 0x8002, L"Arcade King Super sound" }, /* */ { 0xd8, 0x8003, 0x8003, L"SoundOff! sound bank" }, /*DBM*/ { 0xdb, 0x0000, 0xffff, L"DB Master document" }, /* */ { 0xdb, 0x0001, 0x0001, L"DB Master document" }, /*???*/ { 0xdd, 0x0000, 0xffff, L"DDD Deluxe archive" }, // unofficial /*LBR*/ { 0xe0, 0x0000, 0xffff, L"Archival library" }, /* */ { 0xe0, 0x0000, 0x0000, L"ALU library" }, /* */ { 0xe0, 0x0001, 0x0001, L"AppleSingle file" }, /* */ { 0xe0, 0x0002, 0x0002, L"AppleDouble header file" }, /* */ { 0xe0, 0x0003, 0x0003, L"AppleDouble data file" }, /* */ { 0xe0, 0x0004, 0x0004, L"Archiver archive" }, /* */ { 0xe0, 0x0005, 0x0005, L"DiskCopy 4.2 disk image" }, /* */ { 0xe0, 0x0100, 0x0100, L"Apple 5.25 disk image" }, /* */ { 0xe0, 0x0101, 0x0101, L"Profile 5MB disk image" }, /* */ { 0xe0, 0x0102, 0x0102, L"Profile 10MB disk image" }, /* */ { 0xe0, 0x0103, 0x0103, L"Apple 3.5 disk image" }, /* */ { 0xe0, 0x0104, 0x0104, L"SCSI device image" }, /* */ { 0xe0, 0x0105, 0x0105, L"SCSI hard disk image" }, /* */ { 0xe0, 0x0106, 0x0106, L"SCSI tape image" }, /* */ { 0xe0, 0x0107, 0x0107, L"SCSI CD-ROM image" }, /* */ { 0xe0, 0x010e, 0x010e, L"RAM disk image" }, /* */ { 0xe0, 0x010f, 0x010f, L"ROM disk image" }, /* */ { 0xe0, 0x0110, 0x0110, L"File server image" }, /* */ { 0xe0, 0x0113, 0x0113, L"Hard disk image" }, /* */ { 0xe0, 0x0114, 0x0114, L"Floppy disk image" }, /* */ { 0xe0, 0x0115, 0x0115, L"Tape image" }, /* */ { 0xe0, 0x011e, 0x011e, L"AppleTalk file server image" }, /* */ { 0xe0, 0x0120, 0x0120, L"DiskCopy 6 disk image" }, /* */ { 0xe0, 0x0130, 0x0130, L"Universal Disk Image file" }, /* */ { 0xe0, 0x8000, 0x8000, L"Binary II file" }, /* */ { 0xe0, 0x8001, 0x8001, L"AppleLink ACU document" }, /* */ { 0xe0, 0x8002, 0x8002, L"ShrinkIt (NuFX) document" }, /* */ { 0xe0, 0x8003, 0x8003, L"Universal Disk Image file" }, /* */ { 0xe0, 0x8004, 0x8004, L"Davex archived volume" }, /* */ { 0xe0, 0x8006, 0x8006, L"EZ Backup Saveset doc" }, /* */ { 0xe0, 0x8007, 0x8007, L"ELS DOS 3.3 volume" }, /* */ { 0xe0, 0x8008, 0x8008, L"UtilityWorks document" }, /* */ { 0xe0, 0x800a, 0x800a, L"Replicator document" }, /* */ { 0xe0, 0x800b, 0x800b, L"AutoArk compressed document" }, /* */ { 0xe0, 0x800d, 0x800d, L"HardPressed compressed data (data fork)" }, /* */ { 0xe0, 0x800e, 0x800e, L"HardPressed compressed data (rsrc fork)" }, /* */ { 0xe0, 0x800f, 0x800f, L"HardPressed compressed data (both forks)" }, /* */ { 0xe0, 0x8010, 0x8010, L"LHA archive" }, /*ATK*/ { 0xe2, 0x0000, 0xffff, L"AppleTalk data" }, /* */ { 0xe2, 0xffff, 0xffff, L"EasyMount document" }, /*R16*/ { 0xee, 0x0000, 0xffff, L"EDASM 816 relocatable file" }, /*PAS*/ { 0xef, 0x0000, 0xffff, L"Pascal area" }, /*CMD*/ { 0xf0, 0x0000, 0xffff, L"BASIC command" }, /*???*/ { 0xf1, 0x0000, 0xffff, L"User type #1" }, /*???*/ { 0xf2, 0x0000, 0xffff, L"User type #2" }, /*???*/ { 0xf3, 0x0000, 0xffff, L"User type #3" }, /*???*/ { 0xf4, 0x0000, 0xffff, L"User type #4" }, /*???*/ { 0xf5, 0x0000, 0xffff, L"User type #5" }, /*???*/ { 0xf6, 0x0000, 0xffff, L"User type #6" }, /*???*/ { 0xf7, 0x0000, 0xffff, L"User type #7" }, /*???*/ { 0xf8, 0x0000, 0xffff, L"User type #8" }, /*OS */ { 0xf9, 0x0000, 0xffff, L"GS/OS system file" }, /*OS */ { 0xfa, 0x0000, 0xffff, L"Integer BASIC program" }, /*OS */ { 0xfb, 0x0000, 0xffff, L"Integer BASIC variables" }, /*OS */ { 0xfc, 0x0000, 0xffff, L"AppleSoft BASIC program" }, /*OS */ { 0xfd, 0x0000, 0xffff, L"AppleSoft BASIC variables" }, /*OS */ { 0xfe, 0x0000, 0xffff, L"Relocatable code" }, /*OS */ { 0xff, 0x0000, 0xffff, L"ProDOS 8 application" }, }; /* * Find an entry in the type description table that matches both file type and * aux type. If no match is found, nil is returned. */ /*static*/ const WCHAR* PathProposal::FileTypeDescription(long fileType, long auxType) { int i; for (i = NELEM(gTypeDescriptions)-1; i >= 0; i--) { if (fileType == gTypeDescriptions[i].fileType && auxType >= gTypeDescriptions[i].minAuxType && auxType <= gTypeDescriptions[i].maxAuxType) { return gTypeDescriptions[i].descr; } } return nil; } /* * =========================================================================== * Filename/filetype conversion * =========================================================================== */ /* * Convert a pathname pulled out of an archive to something suitable for the * local filesystem. * * The new pathname may be shorter (because characters were removed) or * longer (if we add a "#XXYYYYZ" extension or replace chars with '%' codes). */ void PathProposal::ArchiveToLocal(void) { WCHAR* pathBuf; const WCHAR* startp; const WCHAR* endp; WCHAR* dstp; int newBufLen; /* init output fields */ fLocalFssep = kLocalFssep; fLocalPathName = L""; /* * Set up temporary buffer space. The maximum possible expansion * requires converting all chars to '%' codes and adding the longest * possible preservation string. */ newBufLen = fStoredPathName.GetLength()*3 + kMaxPathGrowth +1; pathBuf = fLocalPathName.GetBuffer(newBufLen); ASSERT(pathBuf != nil); startp = fStoredPathName; dstp = pathBuf; while (*startp == fStoredFssep) { /* ignore leading path sep; always extract to current dir */ startp++; } /* normalize all directory components and the filename component */ while (startp != nil) { endp = nil; if (fStoredFssep != '\0') endp = wcschr(startp, fStoredFssep); if (endp != nil && endp == startp) { /* zero-length subdir component */ WMSG1("WARNING: zero-length subdir component in '%ls'\n", startp); startp++; continue; } if (endp != nil) { /* normalize directory component */ NormalizeDirectoryName(startp, endp - startp, fStoredFssep, &dstp, newBufLen); *dstp++ = fLocalFssep; startp = endp +1; } else { /* normalize filename component */ NormalizeFileName(startp, wcslen(startp), fStoredFssep, &dstp, newBufLen); *dstp++ = '\0'; /* add/replace extension if necessary */ WCHAR extBuf[kMaxPathGrowth +1] = L""; if (fPreservation) { AddPreservationString(pathBuf, extBuf); } else if (fThreadKind == GenericEntry::kRsrcThread) { /* add this in lieu of the preservation extension */ wcscat(pathBuf, kResourceStr); } if (fAddExtension) { AddTypeExtension(pathBuf, extBuf); } ASSERT(wcslen(extBuf) <= kMaxPathGrowth); wcscat(pathBuf, extBuf); startp = nil; /* we're done, break out of loop */ } } /* check for overflow */ ASSERT(dstp - pathBuf <= newBufLen); /* * If "junk paths" is set, drop everything but the last component. */ if (fJunkPaths) { WCHAR* lastFssep; lastFssep = wcsrchr(pathBuf, fLocalFssep); if (lastFssep != nil) { ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ memmove(pathBuf, lastFssep+1, (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); } } fLocalPathName.ReleaseBuffer(); } #if defined(WINDOWS_LIKE) /* * You can't create files or directories with these names on a FAT filesystem, * because they're MS-DOS "device special files". * * The list originally came from the Linux kernel's fs/msdos/namei.c; a * better reference is "Naming Files, Paths, and Namespaces": * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx * * The trick is that the name can't start with any of these. That could mean * that the name is just "aux", or it could be "aux.this.txt". */ static const WCHAR* gFatReservedNames3[] = { L"CON", L"PRN", L"NUL", L"AUX", nil }; static const WCHAR* gFatReservedNames4[] = { L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", nil }; /* * Filename normalization for Win32 filesystems. You can't use [ \/:*?"<>| ] * or control characters, and we're currently avoiding high-ASCII stuff. * TODO: consider supporting the "Mac Roman" characters * * TODO: don't allow the filename to end with a space or period */ void PathProposal::Win32NormalizeFileName(const WCHAR* srcp, long srcLen, char fssep, WCHAR** pDstp, long dstLen) { WCHAR* dstp = *pDstp; const WCHAR* startp = srcp; static const WCHAR* kInvalid = L"\\/:*?\"<>|"; /* match on "aux" or "aux.blah" */ if (srcLen >= 3) { const WCHAR** ppcch; for (ppcch = gFatReservedNames3; *ppcch != nil; ppcch++) { if (wcsnicmp(srcp, *ppcch, 3) == 0 && (srcp[3] == '.' || srcLen == 3)) { WMSG1("--- fixing '%ls'\n", *ppcch); if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = '0'; *dstp++ = '0'; } else *dstp++ = '_'; break; } } } if (srcLen >= 4) { const WCHAR** ppcch; for (ppcch = gFatReservedNames4; *ppcch != nil; ppcch++) { if (wcsnicmp(srcp, *ppcch, 4) == 0 && (srcp[4] == '.' || srcLen == 4)) { WMSG1("--- fixing '%ls'\n", *ppcch); if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = '0'; *dstp++ = '0'; } else *dstp++ = '_'; break; } } } while (srcLen--) { /* don't go until null found! */ ASSERT(*srcp != '\0'); if (*srcp == kForeignIndic) { /* change '%' to "%%" */ if (fPreservation) *dstp++ = *srcp; *dstp++ = *srcp++; } else if (wcschr(kInvalid, *srcp) != nil || *srcp < 0x20 || *srcp >= 0x7f) { /* change invalid char to "%2f" or '_' */ // TODO: this assumes 8-bit input; should convert to UTF-8 if (fPreservation) { *dstp++ = kForeignIndic; *dstp++ = HexConv(*srcp >> 4 & 0x0f); *dstp++ = HexConv(*srcp & 0x0f); } else { *dstp++ = '_'; } srcp++; } else { /* no need to fiddle with it */ *dstp++ = *srcp++; } } *dstp = '\0'; /* end the string, but don't advance past the null */ ASSERT(*pDstp - dstp <= dstLen); /* make sure we didn't overflow */ *pDstp = dstp; } #endif /* * Normalize a file name to local filesystem conventions. The input * is quite possibly *NOT* null-terminated, since it may represent a * substring of a full pathname. Use "srcLen". * * The output filename is copied to *pDstp, which is advanced forward. * * The output buffer must be able to hold 3x the original string length. */ void PathProposal::NormalizeFileName(const WCHAR* srcp, long srcLen, char fssep, WCHAR** pDstp, long dstLen) { ASSERT(srcp != nil); ASSERT(srcLen > 0); ASSERT(dstLen > srcLen); ASSERT(pDstp != nil); ASSERT(*pDstp != nil); #if defined(UNIX_LIKE) UNIXNormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); #elif defined(WINDOWS_LIKE) Win32NormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); #else #error "port this" #endif } /* * Normalize a directory name to local filesystem conventions. */ void PathProposal::NormalizeDirectoryName(const WCHAR* srcp, long srcLen, char fssep, WCHAR** pDstp, long dstLen) { /* in general, directories and filenames are the same */ ASSERT(fssep > ' ' && fssep < 0x7f); NormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); } /* * Add a preservation string. * * "pathBuf" is assumed to have enough space to hold the current path * plus kMaxPathGrowth more. It will be modified in place. */ void PathProposal::AddPreservationString(const WCHAR* pathBuf, WCHAR* extBuf) { WCHAR* cp; ASSERT(pathBuf != nil); ASSERT(extBuf != nil); ASSERT(fPreservation); cp = extBuf + wcslen(extBuf); /* * Cons up a preservation string. On some platforms "sprintf" doesn't * return the #of characters written, so we add it up manually. */ if (fFileType < 0x100 && fAuxType < 0x10000) { wsprintf(cp, L"%c%02lx%04lx", kPreserveIndic, fFileType, fAuxType); cp += 7; } else { wsprintf(cp, L"%c%08lx%08lx", kPreserveIndic, fFileType, fAuxType); cp += 17; } if (fThreadKind == GenericEntry::kRsrcThread) *cp++ = kResourceFlag; else if (fThreadKind == GenericEntry::kDiskImageThread) *cp++ = kDiskImageFlag; /* make sure it's terminated */ *cp = '\0'; } /* * Add a ".foo" extension to the filename. * * We either need to retain the existing extension (possibly obscured by file * type preservation) or append an extension based on the ProDOS file type. */ void PathProposal::AddTypeExtension(const WCHAR* pathBuf, WCHAR* extBuf) { const WCHAR* pPathExt = nil; const WCHAR* pWantedExt = nil; const WCHAR* pTypeExt = nil; WCHAR* end; WCHAR* cp; cp = extBuf + wcslen(extBuf); /* * Find extension in the local filename that we've prepared so far. * Note FindExtension guarantees there's at least one char after '.'. */ pPathExt = PathName::FindExtension(pathBuf, fLocalFssep); if (pPathExt == nil) { /* * There's no extension on the filename. Use the standard * ProDOS type, if one exists for this entry. We don't use * the table if it's NON, "???", or a hex value. */ if (fFileType) { pTypeExt = FileTypeString(fFileType); if (pTypeExt[0] == '?' || pTypeExt[0] == '$') pTypeExt = nil; } } else { pPathExt++; // skip leading '.' } /* * Figure out what extension we want this file to have. Files of type * text are *always* ".TXT", and our extracted disk images are always * ".PO". If it's not one of these two, we either retain the file's * original extension, or generate one for it from the ProDOS file type. */ if (fFileType == 0x04) pWantedExt = L"TXT"; else if (fThreadKind == GenericEntry::kDiskImageThread) pWantedExt = L"PO"; else { /* * We want to use the extension currently on the file, if it has one. * If not, use the one from the file type. */ if (pPathExt != nil) { pWantedExt = pPathExt; } else { pWantedExt = pTypeExt; } } /* pWantedExt != nil unless we failed to find a pTypeExt */ /* * Now we know which one we want. Figure out if we want to add it. */ if (pWantedExt != nil) { if (extBuf[0] == '\0' && pPathExt != nil && wcsicmp(pPathExt, pWantedExt) == 0) { /* don't add an extension that's already there */ pWantedExt = nil; goto know_ext; } if (wcslen(pWantedExt) >= kMaxExtLen) { /* too long, forget it */ pWantedExt = nil; goto know_ext; } /* if it's strictly decimal-numeric, don't use it (.1, .2, etc) */ (void) wcstoul(pWantedExt, &end, 10); if (*end == '\0') { pWantedExt = nil; goto know_ext; } /* if '#' appears in it, don't use it -- it'll confuse us */ //WMSG2("LOOKING FOR '%c' in '%ls'\n", kPreserveIndic, ccp); const WCHAR* ccp = pWantedExt; while (*ccp != '\0') { if (*ccp == kPreserveIndic) { pWantedExt = nil; goto know_ext; } ccp++; } } know_ext: /* * If pWantedExt is non-nil, it points to a filename extension without * the leading '.'. */ if (pWantedExt != nil) { *cp++ = kFilenameExtDelim; wcscpy(cp, pWantedExt); //cp += strlen(pWantedExt); } } /* * =========================================================================== * File type restoration * =========================================================================== */ typedef bool Boolean; /* * Convert a local path into something suitable for storage in an archive. * Type preservation strings are interpreted and stripped as appropriate. * * This does *not* do filesystem-specific normalization here. (It could, but * it's better to leave that for later so we can do uniqueification.) * * In the current implementation, fStoredPathName will always get smaller, * but it would be unwise to rely on that. */ void PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) { Boolean wasPreserved; Boolean doJunk = false; Boolean adjusted; WCHAR slashDotDotSlash[5] = L"_.._"; fStoredPathName = fLocalPathName; WCHAR* livePathStr = fStoredPathName.GetBuffer(0); fStoredFssep = kDefaultStoredFssep; /* convert '/' to '\' */ ReplaceFssep(livePathStr, kAltLocalFssep, //NState_GetAltSystemPathSeparator(pState), kLocalFssep, //NState_GetSystemPathSeparator(pState), kLocalFssep); //NState_GetSystemPathSeparator(pState)); /* * Check for file type preservation info in the filename. If present, * set the file type values and truncate the filename. */ wasPreserved = false; if (pAddOpts->fTypePreservation == AddFilesDialog::kPreserveTypes || pAddOpts->fTypePreservation == AddFilesDialog::kPreserveAndExtend) { wasPreserved = ExtractPreservationString(livePathStr); } /* * Do a "denormalization" pass, where we convert invalid chars (such * as '/') from percent-codes back to 8-bit characters. The filename * will always be the same size or smaller, so we can do it in place. */ if (wasPreserved) DenormalizePath(livePathStr); /* * If we're in "extended" mode, and the file wasn't preserved, take a * guess at what the file type should be based on the file extension. */ if (!wasPreserved && pAddOpts->fTypePreservation == AddFilesDialog::kPreserveAndExtend) { InterpretExtension(livePathStr); } if (fStripDiskImageSuffix) StripDiskImageSuffix(livePathStr); /* * Strip bad chars off the front of the pathname. Every time we * remove one thing we potentially expose another, so we have to * loop until it's sanitized. * * The outer loop isn't really necessary under Win32, because you'd * need to do something like ".\\foo", which isn't allowed. UNIX * silently allows ".//foo", so this is a problem there. (We could * probably do away with the inner loops, but those were already * written when I saw the larger problem.) */ do { adjusted = false; /* * Check for other unpleasantness, such as a leading fssep. */ ASSERT(kLocalFssep != '\0'); while (livePathStr[0] == kLocalFssep) { /* slide it down, len is (strlen +1), -1 (dropping first char)*/ memmove(livePathStr, livePathStr+1, wcslen(livePathStr) * sizeof(WCHAR)); adjusted = true; } /* * Remove leading "./". */ while (livePathStr[0] == '.' && livePathStr[1] == kLocalFssep) { /* slide it down, len is (strlen +1) -2 (dropping two chars) */ memmove(livePathStr, livePathStr+2, (wcslen(livePathStr)-1) * sizeof(WCHAR)); adjusted = true; } } while (adjusted); /* * If there's a "/../" present anywhere in the name, junk everything * but the filename. * * This won't catch "foo/bar/..", but that should've been caught as * a directory anyway. */ slashDotDotSlash[0] = kLocalFssep; slashDotDotSlash[3] = kLocalFssep; if ((livePathStr[0] == '.' && livePathStr[1] == '.') || (wcsstr(livePathStr, slashDotDotSlash) != nil)) { WMSG1("Found dot dot in '%ls', keeping only filename\n", livePathStr); doJunk = true; } /* * Scan for and remove "/./" and trailing "/.". They're filesystem * no-ops that work just fine under Win32 and UNIX but could confuse * a IIgs. (Of course, the user could just omit them from the pathname.) */ /* TO DO 20030208 */ /* * If "junk paths" is set, drop everything before the last fssep char. */ if (pAddOpts->fStripFolderNames || doJunk) { WCHAR* lastFssep; lastFssep = wcsrchr(livePathStr, kLocalFssep); if (lastFssep != nil) { ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ memmove(livePathStr, lastFssep+1, (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); } } /* * Finally, substitute our generally-accepted path separator in place of * the local one, stomping on anything with a ':' in it as we do. The * goal is to avoid having "subdir:foo/bar" turn into "subdir/foo/bar", * so we change it to "subdirXfoo:bar". Were we a general-purpose * archiver, this might be a mistake, but we're not. NuFX doesn't really * give us a choice. */ ReplaceFssep(livePathStr, kLocalFssep, PathProposal::kDefaultStoredFssep, 'X'); /* let the CString manage itself again */ fStoredPathName.ReleaseBuffer(); } /* * Replace "oldc" with "newc". If we find an instance of "newc" already * in the string, replace it with "newSubst". */ void PathProposal::ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst) { while (*str != '\0') { if (*str == oldc) *str = newc; else if (*str == newc) *str = newSubst; str++; } } /* * Try to figure out what file type is associated with a filename extension. * * This checks the standard list of ProDOS types (which should catch things * like "TXT" and "BIN") and the separate list of recognized extensions. */ void PathProposal::LookupExtension(const WCHAR* ext) { WCHAR uext3[4]; int i, extLen; extLen = wcslen(ext); ASSERT(extLen > 0); /* * First step is to try to find it in the recognized types list. */ for (i = 0; i < NELEM(gRecognizedExtensions); i++) { if (wcsicmp(ext, gRecognizedExtensions[i].label) == 0) { fFileType = gRecognizedExtensions[i].fileType; fAuxType = gRecognizedExtensions[i].auxType; goto bail; } } /* * Second step is to try to find it in the ProDOS types list. * * The extension is converted to upper case and padded with spaces. * * [do we want to obstruct matching on things like '$f7' here?] */ if (extLen <= 3) { for (i = 2; i >= extLen; i--) uext3[i] = ' '; for ( ; i >= 0; i--) uext3[i] = toupper(ext[i]); uext3[3] = '\0'; for (i = 0; i < NELEM(gFileTypeNames); i++) { if (wcscmp(uext3, gFileTypeNames[i]) == 0) { fFileType = i; goto bail; } } } bail: return; } /* * Try to associate some meaning with the file extension. */ void PathProposal::InterpretExtension(const WCHAR* pathName) { const WCHAR* pExt; ASSERT(pathName != nil); pExt = PathName::FindExtension(pathName, fLocalFssep); if (pExt != nil) LookupExtension(pExt+1); } /* * Check to see if there's a preservation string on the filename. If so, * set the filetype and auxtype information, and trim the preservation * string off. * * We have to be careful not to trip on false-positive occurrences of '#' * in the filename. */ Boolean PathProposal::ExtractPreservationString(WCHAR* pathname) { WCHAR numBuf[9]; unsigned long fileType, auxType; int threadMask; WCHAR* pPreserve; WCHAR* cp; int digitCount; ASSERT(pathname != nil); pPreserve = wcsrchr(pathname, kPreserveIndic); if (pPreserve == nil) return false; /* count up the #of hex digits */ digitCount = 0; for (cp = pPreserve+1; *cp != '\0' && isxdigit((int)*cp); cp++) digitCount++; /* extract the file and aux type */ switch (digitCount) { case 6: /* ProDOS 1-byte type and 2-byte aux */ memcpy(numBuf, pPreserve+1, 2 * sizeof(WCHAR)); numBuf[2] = 0; fileType = wcstoul(numBuf, &cp, 16); ASSERT(cp == numBuf + 2); auxType = wcstoul(pPreserve+3, &cp, 16); ASSERT(cp == pPreserve + 7); break; case 16: /* HFS 4-byte type and 4-byte creator */ memcpy(numBuf, pPreserve+1, 8 * sizeof(WCHAR)); numBuf[8] = 0; fileType = wcstoul(numBuf, &cp, 16); ASSERT(cp == numBuf + 8); auxType = wcstoul(pPreserve+9, &cp, 16); ASSERT(cp == pPreserve + 17); break; default: /* not valid */ return false; } /* check for a threadID specifier */ //threadID = kNuThreadIDDataFork; threadMask = GenericEntry::kDataThread; switch (*cp) { case kResourceFlag: //threadID = kNuThreadIDRsrcFork; threadMask = GenericEntry::kRsrcThread; cp++; break; case kDiskImageFlag: //threadID = kNuThreadIDDiskImage; threadMask = GenericEntry::kDiskImageThread; cp++; break; default: /* do nothing... yet */ break; } /* make sure we were the very last component */ switch (*cp) { case kFilenameExtDelim: /* redundant "-ee" extension */ case '\0': /* end of string! */ break; default: return false; } /* truncate the original string, and return what we got */ *pPreserve = '\0'; fFileType = fileType; fAuxType = auxType; fThreadKind = threadMask; //*pThreadID = threadID; return true; } /* * Remove NuLib2's normalization magic (e.g. "%2f" for '/'). * * This always results in the filename staying the same length or getting * smaller, so we can do it in place in the buffer. */ void PathProposal::DenormalizePath(WCHAR* pathBuf) { const WCHAR* srcp; WCHAR* dstp; WCHAR ch; srcp = pathBuf; dstp = pathBuf; while (*srcp != '\0') { if (*srcp == kForeignIndic) { srcp++; if (*srcp == kForeignIndic) { *dstp++ = kForeignIndic; srcp++; } else if (isxdigit((int)*srcp)) { ch = HexDigit(*srcp) << 4; srcp++; if (isxdigit((int)*srcp)) { /* valid, output char */ ch += HexDigit(*srcp); *dstp++ = ch; srcp++; } else { /* bogus '%' with trailing hex digit found! */ *dstp++ = kForeignIndic; *dstp++ = *(srcp-1); } } else { /* bogus lone '%s' found! */ *dstp++ = kForeignIndic; } } else { *dstp++ = *srcp++; } } *dstp = '\0'; ASSERT(dstp <= srcp); } /* * Remove a disk image suffix. * * Useful when adding disk images directly from a .SDK or .2MG file. We * don't want them to retain their original suffix. */ void PathProposal::StripDiskImageSuffix(WCHAR* pathName) { static const WCHAR diskExt[][4] = { L"SHK", L"SDK", L"IMG", L"PO", L"DO", L"2MG", L"DSK" }; const WCHAR* pExt; int i; pExt = PathName::FindExtension(pathName, fLocalFssep); if (pExt == nil || pExt == pathName) return; for (i = 0; i < NELEM(diskExt); i++) { if (wcsicmp(pExt+1, diskExt[i]) == 0) { WMSG2("Dropping '%ls' from '%ls'\n", pExt, pathName); *const_cast(pExt) = '\0'; return; } } }