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.array_algorithm : 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 {
128     scanMagicFiles(`/home/per/ware/file/magic/Magdir/`);
129 }