Various improvements by Carl Nettelblad.
[reactos.git] / rosapps / cmd / history.c
1 /*
2 * HISTORY.C - command line history.
3 *
4 *
5 * History:
6 *
7 * 14/01/95 (Tim Norman)
8 * started.
9 *
10 * 08/08/95 (Matt Rains)
11 * i have cleaned up the source code. changes now bring this source
12 * into guidelines for recommended programming practice.
13 *
14 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
15 * added config.h include
16 *
17 * 25-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
18 * Cleanup!
19 * Unicode and redirection safe!
20 *
21 * 25-Jan-1999 (Paolo Pantaleo <paolopan@freemail.it>)
22 * Added lots of comments (beginning studying the source)
23 * Added command.com's F3 support (see cmdinput.c)
24 *
25 */
26
27
28
29 /*
30 * HISTORY.C - command line history. Second version
31 *
32 *
33 * History:
34 *
35 * 06/12/99 (Paolo Pantaleo <paolopan@freemail.it>)
36 * started.
37 *
38 */
39
40
41
42
43
44
45 #include "config.h"
46
47
48 #ifdef FEATURE_HISTORY
49
50 #include <windows.h>
51 #include <tchar.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <ctype.h>
55
56 #include "cmd.h"
57
58
59 typedef struct tagHISTORY
60 {
61 struct tagHISTORY *prev;
62 struct tagHISTORY *next;
63 LPTSTR string;
64 } HIST_ENTRY, * LPHIST_ENTRY;
65
66 static INT size,
67 max_size=100;
68
69
70
71 static LPHIST_ENTRY Top;
72 static LPHIST_ENTRY Bottom;
73
74
75 static LPHIST_ENTRY curr_ptr=0;
76
77
78 VOID InitHistory(VOID);
79 VOID History_move_to_bottom(VOID);
80 VOID History (INT dir, LPTSTR commandline);
81 VOID CleanHistory(VOID);
82 VOID History_del_current_entry(LPTSTR str);
83
84 /*service functions*/
85 static VOID del(LPHIST_ENTRY item);
86 static VOID add_at_bottom(LPTSTR string);
87 /*VOID add_before_last(LPTSTR string);*/
88 VOID set_size(INT new_size);
89
90
91
92 INT CommandHistory (LPTSTR cmd, LPTSTR param)
93 {
94 LPTSTR tmp;
95 INT tmp_int;
96 LPHIST_ENTRY h_tmp;
97 TCHAR szBuffer[2048];
98
99 tmp=_tcschr(param,_T('/'));
100
101 if (tmp)
102 {
103 param=tmp;
104 switch (_totupper(param[1]))
105 {
106 case _T('F'):/*delete history*/
107 CleanHistory();InitHistory();
108 break;
109
110 case _T('R'):/*read history from standard in*/
111 //hIn=GetStdHandle (STD_INPUT_HANDLE);
112
113 for(;;)
114 {
115 ConInString(szBuffer,sizeof(szBuffer)/sizeof(TCHAR));
116 if (*szBuffer!=_T('\0'))
117 History(0,szBuffer);
118 else
119 break;
120 }
121 break;
122
123 case _T('A'):/*add an antry*/
124 History(0,param+2);
125 break;
126
127 case _T('S'):/*set history size*/
128 if ((tmp_int=_ttoi(param+2)))
129 set_size(tmp_int);
130 break;
131
132 default:
133 return 1;
134 }
135 }
136 else
137 {
138 for(h_tmp=Top->prev;h_tmp!=Bottom;h_tmp=h_tmp->prev)
139 ConErrPuts(h_tmp->string);
140 }
141 return 0;
142 }
143
144 VOID set_size(INT new_size)
145 {
146
147 while(new_size<size)
148 del(Top->prev);
149
150
151 max_size=new_size;
152 }
153
154
155 VOID InitHistory(VOID)
156 {
157 size=0;
158
159
160 Top = malloc(sizeof(HIST_ENTRY));
161 Bottom = malloc(sizeof(HIST_ENTRY));
162
163
164 Top->prev = Bottom;
165 Top->next = NULL;
166 Top->string = NULL;
167
168
169 Bottom->prev = NULL;
170 Bottom->next = Top;
171 Bottom->string = NULL;
172
173 curr_ptr=Bottom;
174 }
175
176
177
178
179 VOID CleanHistory(VOID)
180 {
181
182 while (Bottom->next!=Top)
183 del(Bottom->next);
184
185 free(Top);
186 free(Bottom);
187
188 }
189
190
191 VOID History_del_current_entry(LPTSTR str)
192 {
193 LPHIST_ENTRY tmp;
194
195 if (size==0)
196 return;
197
198 if(curr_ptr==Bottom)
199 curr_ptr=Bottom->next;
200
201 if(curr_ptr==Top)
202 curr_ptr=Top->prev;
203
204
205 tmp=curr_ptr;
206 curr_ptr=curr_ptr->prev;
207 del(tmp);
208 History(-1,str);
209
210 }
211
212
213 static
214 VOID del(LPHIST_ENTRY item)
215 {
216
217 if( item==NULL || item==Top || item==Bottom )
218 {
219 #ifdef _DEBUG
220 DebugPrintf("del in " __FILE__ ": retrning\n"
221 "item is 0x%08x (Bottom is0x%08x)\n",
222 item, Bottom);
223
224 #endif
225 return;
226 }
227
228
229
230 /*free string's mem*/
231 if (item->string)
232 free(item->string);
233
234
235
236
237
238 /*set links in prev and next item*/
239 item->next->prev=item->prev;
240 item->prev->next=item->next;
241
242 free(item);
243
244 size--;
245
246 }
247
248 #if 0
249 static
250 VOID add_before_last(LPTSTR string)
251 {
252
253 LPHIST_ENTRY tmp,before,after;
254
255
256 /*delete first entry if maximum number of entries is reached*/
257 while(size>=max_size)
258 del(Top->prev);
259
260 while (_istspace(*string))
261 string++;
262
263 if (*string==_T('\0'))
264 return;
265
266
267
268 /*allocte entry and string*/
269 tmp=malloc(sizeof(HIST_ENTRY));
270 tmp->string=malloc(_tcslen(string)+1);
271 _tcscpy(tmp->string,string);
272
273
274 /*set links*/
275 before=Bottom->next;
276 after=before->next;
277
278 tmp->prev=before;
279 tmp->next=after;
280
281 after->prev=tmp;
282 before->next=tmp;
283
284
285
286
287
288
289
290 /*set new size*/
291 size++;
292
293
294 }
295 #endif/*0*/
296
297 static
298 VOID add_at_bottom(LPTSTR string)
299 {
300
301
302 LPHIST_ENTRY tmp;
303
304
305 /*delete first entry if maximum number of entries is reached*/
306 while(size>=max_size)
307 del(Top->prev);
308
309 while (_istspace(*string))
310 string++;
311
312 if (*string==_T('\0'))
313 return;
314
315
316 /*if new entry is the same than the last do not add it*/
317 if(size)
318 if(_tcscmp(string,Bottom->next->string)==0)
319 return;
320
321
322 /*fill bottom with string, it will become Bottom->next*/
323 Bottom->string=malloc(_tcslen(string)+1);
324 _tcscpy(Bottom->string,string);
325
326 /*save Bottom value*/
327 tmp=Bottom;
328
329
330 /*create new void Bottom*/
331 Bottom=malloc(sizeof(HIST_ENTRY));
332 Bottom->next=tmp;
333 Bottom->prev=NULL;
334 Bottom->string=NULL;
335
336 tmp->prev=Bottom;
337
338 /*set new size*/
339 size++;
340
341 }
342
343
344
345 VOID History_move_to_bottom(VOID)
346 {
347 curr_ptr=Bottom;
348
349 }
350
351
352 VOID History (INT dir, LPTSTR commandline)
353 {
354
355 if(dir==0)
356 {
357 add_at_bottom(commandline);
358 curr_ptr=Bottom;
359 return;
360 }
361
362 if (size==0)
363 {
364 commandline[0]=_T('\0');
365 return;
366 }
367
368
369 if(dir<0)/*key up*/
370 {
371 if (curr_ptr->next==Top || curr_ptr==Top)
372 {
373 #ifdef WRAP_HISTORY
374 curr_ptr=Bottom;
375 #else
376 curr_ptr=Top;
377 commandline[0]=_T('\0');
378 return;
379 #endif
380 }
381
382
383 curr_ptr = curr_ptr->next;
384 if(curr_ptr->string)
385 _tcscpy(commandline,curr_ptr->string);
386
387 }
388
389
390
391
392
393 if(dir>0)
394 {
395
396 if (curr_ptr->prev==Bottom || curr_ptr==Bottom)
397 {
398 #ifdef WRAP_HISTORY
399 curr_ptr=Top;
400 #else
401 curr_ptr=Bottom;
402 commandline[0]=_T('\0');
403 return;
404 #endif
405 }
406
407 curr_ptr=curr_ptr->prev;
408 if(curr_ptr->string)
409 _tcscpy(commandline,curr_ptr->string);
410
411 }
412 }
413
414
415
416
417
418
419 #if 0
420
421 LPTSTR history = NULL; /*buffer to sotre all the lines*/
422 LPTSTR lines[MAXLINES]; /*array of pointers to each line(entry)*/
423 /*located in history buffer*/
424
425 INT curline = 0; /*the last line recalled by user*/
426 INT numlines = 0; /*number of entries, included the last*/
427 /*empty one*/
428
429 INT maxpos = 0; /*index of last byte of last entry*/
430
431
432
433 VOID History (INT dir, LPTSTR commandline)
434 {
435
436 INT count; /*used in for loops*/
437 INT length; /*used in the same loops of count*/
438 /*both to make room when is full
439 either history or lines*/
440
441 /*first time History is called allocate mem*/
442 if (!history)
443 {
444 history = malloc (history_size * sizeof (TCHAR));
445 lines[0] = history;
446 history[0] = 0;
447 }
448
449 if (dir > 0)
450 {
451 /* next command */
452 if (curline < numlines)
453 {
454 curline++;
455 }
456
457 if (curline == numlines)
458 {
459 commandline[0] = 0;
460 }
461 else
462 {
463 _tcscpy (commandline, lines[curline]);
464 }
465 }
466 else if (dir < 0)
467 {
468 /* prev command */
469 if (curline > 0)
470 {
471 curline--;
472 }
473
474 _tcscpy (commandline, lines[curline]);
475 }
476 else
477 {
478 /* add to history */
479 /* remove oldest string until there's enough room for next one */
480 /* strlen (commandline) must be less than history_size! */
481 while ((maxpos + (INT)_tcslen (commandline) + 1 > history_size) || (numlines >= MAXLINES))
482 {
483 length = _tcslen (lines[0]) + 1;
484
485 for (count = 0; count < maxpos && count + (lines[1] - lines[0]) < history_size; count++)
486 {
487 history[count] = history[count + length];
488 }
489
490 maxpos -= length;
491
492 for (count = 0; count <= numlines && count < MAXLINES; count++)
493 {
494 lines[count] = lines[count + 1] - length;
495 }
496
497 numlines--;
498 #ifdef DEBUG
499 ConOutPrintf (_T("Reduced size: %ld lines\n"), numlines);
500
501 for (count = 0; count < numlines; count++)
502 {
503 ConOutPrintf (_T("%d: %s\n"), count, lines[count]);
504 }
505 #endif
506 }
507
508 /*copy entry in the history bufer*/
509 _tcscpy (lines[numlines], commandline);
510 numlines++;
511
512 /*set last lines[numlines] pointer next the end of last, valid,
513 just setted entry (the two lines above)*/
514 lines[numlines] = lines[numlines - 1] + _tcslen (commandline) + 1;
515 maxpos += _tcslen (commandline) + 1;
516 /* last line, empty */
517
518 curline = numlines;
519 }
520
521 return;
522 }
523
524 #endif
525
526 #endif //#if 0