xref: /reactos/base/shell/cmd/history.c (revision cc439606)
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 = NULL;
54 static LPHIST_ENTRY Bottom = NULL;
55 
56 static LPHIST_ENTRY curr_ptr = NULL;
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     ASSERT(Top && Bottom);
124 
125     while (new_size<size)
126         del(Top->prev);
127 
128     max_size=new_size;
129 }
130 
131 
132 VOID InitHistory(VOID)
133 {
134     size = 0;
135 
136     Top = cmd_alloc(sizeof(HIST_ENTRY));
137     if (!Top)
138     {
139         WARN("Cannot allocate memory for Top!\n");
140         return;
141     }
142     Bottom = cmd_alloc(sizeof(HIST_ENTRY));
143     if (!Bottom)
144     {
145         WARN("Cannot allocate memory for Bottom!\n");
146         cmd_free(Top);
147         Top = NULL;
148         return;
149     }
150 
151     Top->prev = Bottom;
152     Top->next = NULL;
153     Top->string = NULL;
154 
155     Bottom->prev = NULL;
156     Bottom->next = Top;
157     Bottom->string = NULL;
158 
159     curr_ptr = Bottom;
160 }
161 
162 
163 VOID CleanHistory(VOID)
164 {
165     ASSERT(Top && Bottom);
166 
167     while (Bottom->next != Top)
168         del(Bottom->next);
169 
170     cmd_free(Top);
171     cmd_free(Bottom);
172 }
173 
174 
175 VOID History_del_current_entry(LPTSTR str)
176 {
177     LPHIST_ENTRY tmp;
178 
179     ASSERT(Top && Bottom);
180 
181     if (size == 0)
182         return;
183 
184     if (curr_ptr == Bottom)
185         curr_ptr = Bottom->next;
186 
187     if (curr_ptr == Top)
188         curr_ptr = Top->prev;
189 
190 
191     tmp = curr_ptr;
192     curr_ptr = curr_ptr->prev;
193     del(tmp);
194     History(-1, str);
195 }
196 
197 
198 static
199 VOID del(LPHIST_ENTRY item)
200 {
201     ASSERT(Top && Bottom);
202 
203     if (item==NULL || item==Top || item==Bottom)
204     {
205         TRACE ("del in " __FILE__ ": returning\n"
206                 "item is 0x%08x (Bottom is0x%08x)\n",
207                 item, Bottom);
208         return;
209     }
210 
211     /*free string's mem*/
212     if (item->string)
213         cmd_free(item->string);
214 
215     /*set links in prev and next item*/
216     item->next->prev=item->prev;
217     item->prev->next=item->next;
218 
219     cmd_free(item);
220 
221     size--;
222 }
223 
224 static
225 VOID add_at_bottom(LPTSTR string)
226 {
227     LPHIST_ENTRY tmp;
228 
229     ASSERT(Top && Bottom);
230 
231     /*delete first entry if maximum number of entries is reached*/
232     while (size>=max_size)
233         del(Top->prev);
234 
235     while (_istspace(*string))
236         string++;
237 
238     if (*string==_T('\0'))
239         return;
240 
241     /*if new entry is the same than the last do not add it*/
242     if (size)
243     {
244         if (_tcscmp(string,Bottom->next->string)==0)
245             return;
246     }
247 
248     /*create new empty Bottom*/
249     tmp = cmd_alloc(sizeof(HIST_ENTRY));
250     if (!tmp)
251     {
252         WARN("Cannot allocate memory for new Bottom!\n");
253         return;
254     }
255 
256     /*fill old bottom with string, it will become new Bottom->next*/
257     Bottom->string = cmd_alloc((_tcslen(string)+1)*sizeof(TCHAR));
258     if (!Bottom->string)
259     {
260         WARN("Cannot allocate memory for Bottom->string!\n");
261         cmd_free(tmp);
262         return;
263     }
264     _tcscpy(Bottom->string,string);
265 
266     tmp->next = Bottom;
267     tmp->prev = NULL;
268     tmp->string = NULL;
269 
270     Bottom->prev = tmp;
271 
272     /*save the new Bottom value*/
273     Bottom = tmp;
274 
275     /*set new size*/
276     size++;
277 }
278 
279 
280 VOID History_move_to_bottom(VOID)
281 {
282     ASSERT(Top && Bottom);
283 
284     curr_ptr = Bottom;
285 }
286 
287 LPCTSTR PeekHistory(INT dir)
288 {
289     LPHIST_ENTRY entry = curr_ptr;
290 
291     ASSERT(Top && Bottom);
292 
293     if (dir == 0)
294         return NULL;
295 
296     if (dir < 0)
297     {
298         /* key up */
299         if (entry->next == Top || entry == Top)
300         {
301 #ifdef WRAP_HISTORY
302             entry = Bottom;
303 #else
304             return NULL;
305 #endif
306         }
307         entry = entry->next;
308     }
309     else
310     {
311         /* key down */
312         if (entry->next == Bottom || entry == Bottom)
313         {
314 #ifdef WRAP_HISTORY
315             entry = Top;
316 #else
317             return NULL;
318 #endif
319         }
320         entry = entry->prev;
321     }
322 
323     return entry->string;
324 }
325 
326 VOID History(INT dir, LPTSTR commandline)
327 {
328     ASSERT(Top && Bottom);
329 
330     if (dir==0)
331     {
332         add_at_bottom(commandline);
333         curr_ptr = Bottom;
334         return;
335     }
336 
337     if (size==0)
338     {
339         commandline[0]=_T('\0');
340         return;
341     }
342 
343     if (dir<0)/*key up*/
344     {
345         if (curr_ptr->next==Top || curr_ptr==Top)
346         {
347 #ifdef WRAP_HISTORY
348             curr_ptr = Bottom;
349 #else
350             curr_ptr = Top;
351             commandline[0]=_T('\0');
352             return;
353 #endif
354         }
355 
356         curr_ptr = curr_ptr->next;
357         if (curr_ptr->string)
358             _tcscpy(commandline,curr_ptr->string);
359     }
360 
361     if (dir>0)
362     {
363         if (curr_ptr->prev==Bottom || curr_ptr==Bottom)
364         {
365 #ifdef WRAP_HISTORY
366             curr_ptr = Top;
367 #else
368             curr_ptr = Bottom;
369             commandline[0]=_T('\0');
370             return;
371 #endif
372         }
373 
374         curr_ptr = curr_ptr->prev;
375         if (curr_ptr->string)
376             _tcscpy(commandline,curr_ptr->string);
377     }
378 }
379 
380 #endif //#if FEATURE_HISTORY
381