1 module dinu.content.output;
2 
3 import
4 	std.file,
5 	std.datetime,
6 	std.stream,
7 	std.parallelism,
8 	std.process,
9 	std.conv,
10 	std.regex,
11 	std..string,
12 	std.stdio,
13 	dinu.dinu,
14 	dinu.util,
15 	dinu.xclient,
16 	dinu.content.content,
17 	dinu.content.executables,
18 	dinu.content.files,
19 	dinu.command,
20 	dinu.draw;
21 
22 
23 __gshared:
24 
25 
26 private {
27 	CommandHistory[int] running;
28 	int[int] results;
29 }
30 
31 
32 class OutputLoader: ChoiceLoader {
33 
34 	override void run(){
35 		auto log = options.configPath ~ ".log";
36 		while(!log.exists && active && runProgram)
37 			core.thread.Thread.sleep(10.msecs);
38 		auto file = new BufferedFile(log);
39 		file.seekEnd(0);
40 		size_t idx = 10000;
41 		task(&loadBackwards, idx-1000).executeInNewThread;
42 		while(active && runProgram){
43 			if(!file.eof){
44 				auto line = cast(string)file.readLine;
45 				if(!line.length)
46 					continue;
47 				matchLine(line, idx++);
48 			}else
49 				core.thread.Thread.sleep(5.msecs);
50 		}
51 	}
52 
53 	void loadBackwards(size_t idx){
54 		auto p = pipeProcess(["tac", options.configPath ~ ".log"], Redirect.stdout);
55 		foreach(line; p.stdout.byLine){
56 			matchLine(to!string(line), idx--);
57 			if(idx<10000-2000)
58 				core.thread.Thread.sleep(4.msecs);
59 			if(idx==0 || !runProgram)
60 				break;
61 		}
62 	}
63 
64 	void matchLine(string line, size_t idx){
65 		try{
66 			foreach(match; line.matchAll(`([0-9]+) (\S+)(?: (.*))?`)){
67 				auto pid = to!int(match.captures[1]);
68 				if(match.captures[2] == "exec"){
69 					auto cmd = match.captures[3].bangSplit;
70 					auto history = new CommandHistory(idx, pid, to!Type(cmd[0]), cmd[1].dup, cmd[2].dup);
71 					running[pid] = history;
72 					if(pid in results){
73 						history.result = results[pid];
74 					}else if(!exists("/proc/%s".format(pid))){
75 						history.result = 0;
76 					}
77 					add(history);
78 				}
79 				if((match.captures[2] == "stdout" || match.captures[2] == "stderr") && match.captures[3].length){
80 					add(new CommandOutput(pid, match.captures[3], idx, match.captures[2]=="stderr"));
81 				}else if(match.captures[2] == "exit"){
82 					if(pid in running)
83 						running[pid].result = to!int(match.captures[3]);
84 					else
85 						results[pid] = to!int(match.captures[3]);
86 				}
87 			}
88 		}catch(Exception e){
89 			writeln(e);
90 		}
91 	}
92 
93 }
94 
95 
96 class CommandOutput: Command {
97 	
98 	size_t idx;
99 	string command;
100 	int pid;
101 
102 	this(int pid, string output, size_t idx, bool err){
103 		super(output);
104 		type = Type.output;
105 		this.pid = pid;
106 		this.idx = idx;
107 		if(err)
108 			color = options.colorError;
109 		else
110 			color = options.colorOutput;
111 	}
112 
113 	override size_t score(){
114 		return idx*1000;
115 	}
116 
117 	override int draw(DrawingContext dc, int[2] pos, bool selected){
118 		if(!command.length && pid in running)
119 			command = running[pid].text;
120 		dc.text(pos, command, options.colorHint, 1.8);
121 		return super.draw(dc, pos, selected);
122 	}
123 
124 	override void run(){}
125 
126 }
127 
128 
129 class CommandHistory: Command {
130 
131 	size_t idx;
132 	long result = long.max;
133 	Type originalType;
134 	Command command;
135 
136 	this(size_t idx, int pid, Type originalType, string serialized, string parameter){
137 		this.idx = idx;
138 		this.parameter = parameter;
139 		type = Type.history;
140 		switch(originalType){
141 			case Type.script:
142 				command = new CommandExec(serialized);
143 				break;
144 			case Type.desktop:
145 				command = new CommandDesktop(serialized);
146 				break;
147 			case Type.file:
148 				command = new CommandFile(serialized);
149 				break;
150 			case Type.directory:
151 				command = new CommandDir(serialized);
152 				break;
153 			case Type.special:
154 				command = new CommandSpecial(serialized);
155 				break;
156 			default:
157 				command = new CommandExec(serialized);
158 				break;
159 		}
160 		this.name = command.text;
161 	}
162 
163 	override string filterText(){
164 		return super.filterText() ~ parameter;
165 	}
166 
167 	override size_t score(){
168 		return idx*1000;
169 	}
170 
171 	override int draw(DrawingContext dc, int[2] pos, bool selected){
172 		auto origX = pos.x;
173 		if(result != long.max){
174 			if(result)
175 				dc.rect([pos.x-0.4.em,pos.y], [0.1.em, 1.em], options.colorError);
176 		}else
177 			dc.rect([pos.x-0.4.em,pos.y], [0.1.em, 1.em], options.colorHint);
178 		pos.x += command.draw(dc, pos, selected);
179 		if(parameter.length)
180 			pos.x += dc.text(pos, parameter, options.colorOutput);
181 		return pos.x-origX;
182 	}
183 
184 	override void run(){
185 		auto paramOrig = command.parameter;
186 		command.parameter ~= parameter;
187 		command.run;
188 		command.parameter = paramOrig;
189 	}
190 
191 }