1 /** Scan file/magic/Magdir */ 2 module nxt.magics; 3 4 /** Magic Specifier. */ 5 struct Magic 6 { 7 string header; 8 string description; 9 size_t byteOffset; 10 } 11 12 // version = show; 13 14 // import backtrace.backtrace; 15 16 /** Scan Directory $(D dir) for file magic. */ 17 void scanMagicFiles(string dir) 18 { 19 import std.file: dirEntries, SpanMode; 20 import std.stdio: write, writeln, File; 21 import std.array: array; 22 import std.range: front, empty; 23 import std.algorithm : find; 24 import std.ascii: isDigit; 25 import std.uni: isAlpha, isWhite; 26 import std.algorithm.iteration : splitter; 27 28 import nxt.algorithm.searching : startsWith, strip; 29 30 size_t baseCount = 0; 31 size_t attrCount = 0; 32 33 Magic current; 34 35 foreach (file; dir.dirEntries(SpanMode.depth)) 36 { 37 version (show) writeln(`file: `, file.name); 38 foreach (line; File(file).byLine) 39 { 40 // auto parts = line.splitter("\t"); 41 auto parts = line.splitter!(isWhite); 42 if (!parts.empty) // line contains something 43 { 44 if (parts.front.startsWith('#')) // if comment 45 { 46 /* version (show) writeln("comment: ", parts); */ 47 } 48 else // otherwise magic 49 { 50 const first = parts.front; 51 const firstChar = first.front; 52 if (firstChar.isDigit) // base magic 53 { 54 size_t offset; 55 if (first == `0`) // at beginning of file 56 { 57 offset = 0; 58 version (show) write(offset, `-offset-`); 59 parts.popFront(); 60 auto kind = parts.front; 61 switch (kind.strip(' ')) 62 { 63 case `string`: 64 parts.popFront(); 65 auto rest = find!(a => !a.empty)(parts); // skip empty strings 66 if (!rest.empty) 67 { 68 auto magic = rest.front; 69 /+ TODO: Merge these? +/ 70 // import std.array: replaceInPlace; 71 /* magic = magic.replace(`\ `, ` `); */ 72 /* magic = magic.replace(`\r`, "\r"); */ 73 /* magic = magic.replace(`\n`, "\n"); */ 74 /* magic = magic.replace(`\t`, "\t"); */ 75 /+ TODO: Replace `\0`, `\1` +/ 76 /+ TODO: Replace `\OCTAL` with "\OCTAL" +/ 77 /+ TODO: Replace \0xa +/ 78 version (show) writeln(kind, `: `, magic); 79 } 80 break; 81 case `regex`: 82 parts.popFront(); 83 auto rest = find!(a => !a.empty)(parts); // skip empty strings 84 version (show) writeln(kind, `: `, parts); 85 break; 86 case `belong`: // big-endian 64-bit 87 parts.popFront(); 88 auto rest = find!(a => !a.empty)(parts); // skip empty strings 89 version (show) writeln(kind, `: `, parts); 90 break; 91 case `lelong`: // little-endian 64-bit 92 parts.popFront(); 93 auto rest = find!(a => !a.empty)(parts); // skip empty strings 94 version (show) writeln(kind, `: `, parts); 95 break; 96 default: 97 parts.popFront(); 98 auto rest = find!(a => !a.empty)(parts); // skip empty strings 99 version (show) writeln(kind, `: `, parts); 100 break; 101 } 102 baseCount++; 103 104 } 105 else 106 { 107 version (show) writeln("todo: ", parts); 108 } 109 110 } 111 else if (firstChar == '>') 112 { 113 version (show) writeln(`>: `, parts); 114 attrCount++; 115 } 116 } 117 } 118 } 119 } 120 121 version (show) writeln(`Found `, baseCount, ` number of magic bases`); 122 version (show) writeln(`Found `, attrCount, ` number of magic attributes`); 123 } 124 125 version (none) 126 unittest { 127 scanMagicFiles(`/home/per/ware/file/magic/Magdir/`); 128 }