xref: /reactos/base/shell/cmd/history.c (revision 84ccccab)
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)
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 #include "precomp.h"
41 
42 #ifdef FEATURE_HISTORY
43 
44 typedef struct tagHISTORY
45 {
46     struct tagHISTORY *prev;
47     struct tagHISTORY *next;
48     LPTSTR string;
49 } HIST_ENTRY, * LPHIST_ENTRY;
50 
51 static INT size, max_size=100;
52 
53 static LPHIST_ENTRY Top;
54 static LPHIST_ENTRY Bottom;
55 
56 static LPHIST_ENTRY curr_ptr=0;
57 
58 VOID InitHistory(VOID);
59 VOID History_move_to_bottom(VOID);
60 VOID History (INT dir, LPTSTR commandline);
61 VOID CleanHistory(VOID);
62 VOID History_del_current_entry(LPTSTR str);
63 
64 /*service functions*/
65 static VOID del(LPHIST_ENTRY item);
66 static VOID add_at_bottom(LPTSTR string);
67 /*VOID add_before_last(LPTSTR string);*/
68 VOID set_size(INT new_size);
69 
70 
71 INT CommandHistory (LPTSTR param)
72 {
73     LPTSTR tmp;
74     INT tmp_int;
75     LPHIST_ENTRY h_tmp;
76     TCHAR szBuffer[2048];
77 
78     tmp=_tcschr(param,_T('/'));
79 
80     if (tmp)
81     {
82         param=tmp;
83         switch (_totupper(param[1]))
84         {
85             case _T('F'):/*delete history*/
86                 CleanHistory();InitHistory();
87                 break;
88 
89             case _T('R'):/*read history from standard in*/
90                 for(;;)
91                 {
92                     ConInString(szBuffer,sizeof(szBuffer)/sizeof(TCHAR));
93                     if (*szBuffer!=_T('\0'))
94                         History(0,szBuffer);
95                     else
96                         break;
97                 }
98                 break;
99 
100             case _T('A'):/*add an antry*/
101                 History(0,param+2);
102                 break;
103 
104             case _T('S'):/*set history size*/
105                 if ((tmp_int=_ttoi(param+2)))
106                     set_size(tmp_int);
107                 break;
108 
109             default:
110                 return 1;
111         }
112     }
113     else
114     {
115         for (h_tmp = Top->prev; h_tmp != Bottom; h_tmp = h_tmp->prev)
116             ConErrPuts(h_tmp->string);
117     }
118     return 0;
119 }
120 
121 VOID set_size(INT new_size)
122 {
123     while (new_size<size)
124         del(Top->prev);
125 
126     max_size=new_size;
127 }
128 
129 
130 VOID InitHistory(VOID)
131 {
132     size=0;
133 
134     Top = cmd_alloc(sizeof(HIST_ENTRY));
135     Bottom = cmd_alloc(sizeof(HIST_ENTRY));
136 
137     Top->prev = Bottom;
138     Top->next = NULL;
139     Top->string = NULL;
140 
141     Bottom->prev = NULL;
142     Bottom->next = Top;
143     Bottom->string = NULL;
144 
145     curr_ptr=Bottom;
146 }
147 
148 
149 VOID CleanHistory(VOID)
150 {
151     while (Bottom->next!=Top)
152         del(Bottom->next);
153 
154     cmd_free(Top);
155     cmd_free(Bottom);
156 }
157 
158 
159 VOID History_del_current_entry(LPTSTR str)
160 {
161     LPHIST_ENTRY tmp;
162 
163     if (size == 0)
164         return;
165 
166     if (curr_ptr == Bottom)
167         curr_ptr=Bottom->next;
168 
169     if (curr_ptr == Top)
170         curr_ptr=Top->prev;
171 
172 
173     tmp = curr_ptr;
174     curr_ptr = curr_ptr->prev;
175     del(tmp);
176     History(-1, str);
177 }
178 
179 
180 static
181 VOID del(LPHIST_ENTRY item)
182 {
183     if (item==NULL || item==Top || item==Bottom)
184     {
185         TRACE ("del in " __FILE__ ": returning\n"
186                 "item is 0x%08x (Bottom is0x%08x)\n",
187                 item, Bottom);
188         return;
189     }
190 
191     /*free string's mem*/
192     if (item->string)
193         cmd_free(item->string);
194 
195     /*set links in prev and next item*/
196     item->next->prev=item->prev;
197     item->prev->next=item->next;
198 
199     cmd_free(item);
200 
201     size--;
202 }
203 
204 static
205 VOID add_at_bottom(LPTSTR string)
206 {
207     LPHIST_ENTRY tmp;
208 
209     /*delete first entry if maximum number of entries is reached*/
210     while(size>=max_size)
211         del(Top->prev);
212 
213     while (_istspace(*string))
214         string++;
215 
216     if (*string==_T('\0'))
217         return;
218 
219     /*if new entry is the same than the last do not add it*/
220     if (size)
221         if (_tcscmp(string,Bottom->next->string)==0)
222             return;
223 
224     /*fill bottom with string, it will become Bottom->next*/
225     Bottom->string=cmd_alloc((_tcslen(string)+1)*sizeof(TCHAR));
226     _tcscpy(Bottom->string,string);
227 
228     /*save Bottom value*/
229     tmp=Bottom;
230 
231     /*create new void Bottom*/
232     Bottom=cmd_alloc(sizeof(HIST_ENTRY));
233     Bottom->next=tmp;
234     Bottom->prev=NULL;
235     Bottom->string=NULL;
236 
237     tmp->prev=Bottom;
238 
239     /*set new size*/
240     size++;
241 }
242 
243 
244 VOID History_move_to_bottom(VOID)
245 {
246     curr_ptr=Bottom;
247 }
248 
249 LPCTSTR PeekHistory(INT dir)
250 {
251     LPHIST_ENTRY entry = curr_ptr;
252 
253     if (dir == 0)
254         return NULL;
255 
256     if (dir < 0)
257     {
258         /* key up */
259         if (entry->next == Top || entry == Top)
260         {
261 #ifdef WRAP_HISTORY
262             entry = Bottom;
263 #else
264             return NULL;
265 #endif
266         }
267         entry = entry->next;
268     }
269     else
270     {
271         /* key down */
272         if (entry->next == Bottom || entry == Bottom)
273         {
274 #ifdef WRAP_HISTORY
275             entry = Top;
276 #else
277             return NULL;
278 #endif
279         }
280         entry = entry->prev;
281     }
282 
283     return entry->string;
284 }
285 
286 VOID History (INT dir, LPTSTR commandline)
287 {
288     if (dir==0)
289     {
290         add_at_bottom(commandline);
291         curr_ptr=Bottom;
292         return;
293     }
294 
295     if (size==0)
296     {
297         commandline[0]=_T('\0');
298         return;
299     }
300 
301     if (dir<0)/*key up*/
302     {
303         if (curr_ptr->next==Top || curr_ptr==Top)
304         {
305 #ifdef WRAP_HISTORY
306             curr_ptr=Bottom;
307 #else
308             curr_ptr=Top;
309             commandline[0]=_T('\0');
310             return;
311 #endif
312         }
313 
314         curr_ptr = curr_ptr->next;
315         if (curr_ptr->string)
316             _tcscpy(commandline,curr_ptr->string);
317     }
318 
319     if (dir>0)
320     {
321         if (curr_ptr->prev==Bottom || curr_ptr==Bottom)
322         {
323 #ifdef WRAP_HISTORY
324             curr_ptr=Top;
325 #else
326             curr_ptr=Bottom;
327             commandline[0]=_T('\0');
328             return;
329 #endif
330         }
331 
332         curr_ptr=curr_ptr->prev;
333         if (curr_ptr->string)
334             _tcscpy(commandline,curr_ptr->string);
335     }
336 }
337 
338 #endif //#if FEATURE_HISTORY
339