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 }