update for HEAD-2003050101
[reactos.git] / subsys / system / 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