1 /*******************************************************************************
2 * Copyright (c) 2013-2021, Andrés Martinelli <andmarti@gmail.com> *
3 * All rights reserved. *
4 * *
5 * This file is a part of SC-IM *
6 * *
7 * SC-IM is a spreadsheet program that is based on SC. The original authors *
8 * of SC are James Gosling and Mark Weiser, and mods were later added by *
9 * Chuck Martin. *
10 * *
11 * Redistribution and use in source and binary forms, with or without *
12 * modification, are permitted provided that the following conditions are met: *
13 * 1. Redistributions of source code must retain the above copyright *
14 * notice, this list of conditions and the following disclaimer. *
15 * 2. Redistributions in binary form must reproduce the above copyright *
16 * notice, this list of conditions and the following disclaimer in the *
17 * documentation and/or other materials provided with the distribution. *
18 * 3. All advertising materials mentioning features or use of this software *
19 * must display the following acknowledgement: *
20 * This product includes software developed by Andrés Martinelli *
21 * <andmarti@gmail.com>. *
22 * 4. Neither the name of the Andrés Martinelli nor the *
23 * names of other contributors may be used to endorse or promote products *
24 * derived from this software without specific prior written permission. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY ANDRES MARTINELLI ''AS IS'' AND ANY *
27 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
28 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
29 * DISCLAIMED. IN NO EVENT SHALL ANDRES MARTINELLI BE LIABLE FOR ANY *
30 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;*
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
33 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE *
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
36 *******************************************************************************/
37
38 /**
39 * \file history.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 2017-07-18
42 * \brief TODO Write a tbrief file description.
43 */
44
45 #include <string.h>
46 #include <wchar.h>
47 #include "macros.h"
48
49 // current command before tab completion
50 static wchar_t curcmd [BUFFERSIZE];
51
52 /**
53 * \brief TODO Document copy_to_curcmd()
54 *
55 * \param[in] inputline
56 *
57 * \return none
58 */
59
copy_to_curcmd(wchar_t * inputline)60 void copy_to_curcmd(wchar_t * inputline) {
61 wcscpy(curcmd, inputline);
62 }
63
64 /**
65 * \brief TODO Document get_curcmd()
66 *
67 * \return curcmd
68 */
69
get_curcmd()70 wchar_t * get_curcmd() {
71 return curcmd;
72 }
73
74 // this comp mark is used to mark when tab completion is started
75 static int comp = 0;
76
77 /**
78 * \brief TODO Document get_comp()
79 *
80 * \return none
81 */
82
get_comp()83 int get_comp() {
84 return comp;
85 }
86
87 /**
88 * \brief TODO Document set_comp()
89 *
90 * \param[in] i
91 *
92 * \return none
93 */
94
set_comp(int i)95 void set_comp(int i) {
96 comp = i;
97 }
98
99 #if defined HISTORY_FILE
100 #include <stdlib.h>
101 #include <fcntl.h>
102 #include <unistd.h>
103 #include <sys/stat.h>
104
105 #include "history.h"
106 #include "sc.h"
107 #include "utils/string.h"
108
create_history(char mode)109 struct history * create_history(char mode) {
110 struct history * h = (struct history *) malloc (sizeof (struct history));
111 h->len = 0;
112 h->pos = 0;
113 h->mode = mode;
114 h->list = NULL;
115 return h;
116 }
117
118 /**
119 * \brief TODO Document destroy_history()
120 *
121 * \param[in] h
122 *
123 * \return none
124 */
125
destroy_history(struct history * h)126 void destroy_history(struct history * h) {
127 struct hlist * nl;
128 struct hlist * n_sig;
129 nl = h->list;
130 while (nl != NULL) {
131 n_sig = nl->pnext;
132 free(nl->line);
133 free(nl);
134 nl = n_sig;
135 }
136 free(h);
137 return;
138 }
139
140 /**
141 * \brief TODO Document load_history
142 *
143 * \details Read the specified history file from the user's home directory
144 * and load it into the specified history struct.
145 *
146 * \param[in] h
147 * \param[in] mode
148 *
149 * returns: none
150 */
151
load_history(struct history * h,wchar_t mode)152 void load_history(struct history * h, wchar_t mode) {
153 char infofile[PATHLEN];
154 wchar_t linea[FBUFLEN];
155 int c;
156 char * home;
157 FILE * f;
158
159 if ((home = getenv("XDG_CACHE_HOME"))) {
160 /* Try XDG_CACHE_HOME first */
161 sprintf(infofile, "%s/%s", home,HISTORY_FILE);
162 } else if ((home = getenv("HOME"))) {
163 /* Fallback */
164 sprintf(infofile, "%s/%s/%s", home,HISTORY_DIR,HISTORY_FILE);
165 }
166
167 if ((c = open(infofile, O_RDONLY)) > -1) {
168 close(c);
169 f = fopen(infofile, "r");
170 if (f == NULL) return;
171 while ( feof(f) == 0 ) {
172 if (!fgetws(linea, sizeof(linea) / sizeof(*linea), f)) break;
173 int s = wcslen(linea)-1;
174 del_range_wchars(linea, s, s);
175
176 if (linea[0] == mode && mode == L':') {
177 del_range_wchars(linea, 0, 0);
178 add(h, linea);
179 } else if (mode != L':' && (linea[0] == L'=' || linea[0] == L'<' || linea[0] == L'>' || linea[0] == L'\\')) {
180 add(h, linea);
181 }
182 }
183 fclose(f);
184 }
185
186 return;
187 }
188
189 /**
190 * @brief Save history to file
191 *
192 * \return 0 on success; -1 otherwise
193 */
194
save_history(struct history * h,char * mode)195 int save_history(struct history * h, char * mode) {
196 char infofile [PATHLEN];
197 char * home;
198 FILE * f;
199 int i;
200 struct hlist * nl = h->list;
201 char history_dir[PATHLEN-(sizeof HISTORY_FILE)];
202
203 if ((home = getenv("XDG_CACHE_HOME"))) {
204 snprintf(history_dir, PATHLEN-(sizeof HISTORY_FILE), "%s", home);
205 mkdir(history_dir,0777);
206 snprintf(infofile, PATHLEN, "%s/%s", history_dir, HISTORY_FILE);
207 } else if ((home = getenv("HOME"))) {
208 snprintf(history_dir, PATHLEN-(sizeof HISTORY_FILE), "%s/%s", home, HISTORY_DIR);
209 mkdir(history_dir,0777);
210 snprintf(infofile, PATHLEN, "%s/%s", history_dir, HISTORY_FILE);
211 } else {
212 /* If both HOME and XDG_CACHE_HOME aren't set, abandon all hope */
213 return 0;
214 }
215
216 f = fopen(infofile, mode);
217 if (f == NULL) return 0;
218 // Go to the end
219 for (i=1; i < h->len; i++) {
220 nl = nl->pnext;
221 }
222 // Traverse list back to front, so the history is saved in chronological order
223 for (i=0; i < h->len; i++) {
224 if (! strcmp(mode, "w")) fwprintf(f, L":"); // mode 'w' means we are saving the command mode history
225 fwprintf(f, L"%ls\n", nl->line);
226 nl = nl->pant;
227 }
228 fclose(f);
229 return 1;
230 }
231
232 /**
233 * \brief Remove history element
234 *
235 * \return 0 first element; -1 second element
236 */
237
del_item_from_history(struct history * h,int pos)238 void del_item_from_history(struct history * h, int pos) {
239 if (h->len - 1 < -pos) return;
240
241 struct hlist * nl = h->list;
242 struct hlist * n_ant = NULL;
243 int i;
244
245 if (pos == 0) {
246 h->list = nl->pnext;
247 if (nl->pnext != NULL) nl->pnext->pant = NULL;
248 } else {
249 for (i=0; i<-pos; i++) {
250 n_ant = nl;
251 nl = nl->pnext;
252 }
253 n_ant->pnext = nl->pnext;
254 if (nl->pnext != NULL) nl->pnext->pant = n_ant;
255 }
256 free(nl->line);
257 free(nl);
258 h->len--;
259
260 return;
261 }
262
263 /**
264 * \brief TODO <brief function description>
265 *
266 * \details Find a history element and move it. Starts from POS
267 * pos=0 first element, pos=-1 second element. Returns 1 if moved,
268 * 0 otherwise
269 *
270 * \param[in] h
271 * \param[in] item
272 * \param[in] pos
273 *
274 * \returns: 1 if moved; 0 otherwise
275 */
276
move_item_from_history_by_str(struct history * h,wchar_t * item,int pos)277 int move_item_from_history_by_str(struct history * h, wchar_t * item, int pos) {
278 if (h->len - 1 < -pos || pos == 0 || ! wcslen(item)) return 0; // Move the first element is not allowed
279 struct hlist * nl = h->list;
280 if (nl != NULL && !wcscmp(item, nl->line)) return 1;
281 struct hlist * n_ant = NULL;
282 int i;
283
284 for (i=0; i<-pos; i++) {
285 n_ant = nl;
286 nl = nl->pnext;
287 }
288 for (i=-pos; i < h->len ; i++) {
289 if (wcscmp(item, nl->line) == 0) break;
290 n_ant = nl;
291 nl = nl->pnext;
292 }
293 if (i >= h->len) return 0;
294 n_ant->pnext = nl->pnext;
295 if (nl->pnext != NULL) nl->pnext->pant = n_ant;
296
297 nl->pant = NULL;
298 nl->pnext = h->list;
299 h->list->pant = nl;
300 h->list = nl;
301
302 return 1;
303 }
304
305 /**
306 * @brief Add recent entry at the beginning
307 *
308 * \param[in] h
309 * \param[in] line
310 *
311 * \return none
312 */
313
add(struct history * h,wchar_t * line)314 void add(struct history * h, wchar_t * line) {
315 struct hlist * nl = (struct hlist *) malloc(sizeof(struct hlist));
316
317 // Save the line
318 int size = wcslen(line)+1;
319 if (size == 1) size = BUFFERSIZE + 1;
320
321 wchar_t * val = (wchar_t *) malloc(sizeof(wchar_t) * size);
322
323 val[0]=L'\0';
324 wcscpy(val, line);
325 nl->line = val;
326
327 // Append at the beginning
328 nl->pant = NULL;
329 nl->pnext = h->list;
330 if (h->list != NULL) h->list->pant = nl;
331 h->list = nl;
332 h->len++;
333
334 return;
335 }
336
337 /**
338 * \brief Returns
339 *
340 * \details Returns a history line form COMMAND_MODE
341 * POS 0 is the mose recent line
342 *
343 * \return none
344 */
345
get_line_from_history(struct history * h,int pos)346 wchar_t * get_line_from_history(struct history * h, int pos) {
347 return get_hlist_from_history(h, pos)->line;
348 }
349
350 /**
351 * \brief TODO Document get_hlist_from_history()
352 *
353 * \param[in] h
354 * \param[in] pos
355 *
356 * \return none
357 */
358
get_hlist_from_history(struct history * h,int pos)359 struct hlist * get_hlist_from_history(struct history * h, int pos) {
360 if (h->len <= - pos) return NULL;
361 int i;
362 struct hlist * nl = h->list;
363
364 for (i=0; i<-pos; i++) {
365 nl = nl->pnext;
366 }
367 return nl;
368 }
369 #endif
370