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 file.c
40 * \author Andrés Martinelli <andmarti@gmail.com>
41 * \date 2017-07-18
42 * \brief TODO Write a brief file description.
43 */
44
45 #include <pwd.h>
46 #include <sys/stat.h>
47 #include <time.h>
48 #include <utime.h>
49 #include <fcntl.h>
50 #include <signal.h>
51 #include <errno.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <wchar.h>
55 #include <sys/wait.h>
56
57 #ifndef NO_WORDEXP
58 #include <wordexp.h>
59 #endif
60
61 #include "conf.h"
62 #include "maps.h"
63 #include "yank.h"
64 #include "cmds.h"
65 #include "file.h"
66 #include "marks.h"
67 #include "lex.h"
68 #include "format.h"
69 #include "interp.h"
70 #include "utils/string.h"
71 #include "utils/dictionary.h"
72 #include "cmds_edit.h"
73 #include "xmalloc.h"
74 #include "y.tab.h"
75 #include "xlsx.h"
76 #include "ods.h"
77 #include "xls.h"
78 #include "tui.h"
79
80 extern struct ent * freeents;
81 extern int yyparse(void);
82
83 #ifdef HAVE_PTHREAD
84 #include <pthread.h>
85 extern pthread_t fthread;
86 extern int pthread_exists;
87 #endif
88
89 /**
90 * \brief Erase the database (tbl, etc.)
91 *
92 * \return none
93 */
erasedb()94 void erasedb() {
95 int r, c;
96
97 for (c = 0; c <= maxcol; c++) {
98 fwidth[c] = DEFWIDTH;
99 precision[c] = DEFPREC;
100 realfmt[c] = DEFREFMT;
101 }
102
103 for (r = 0; r <= maxrow; r++) {
104 row_format[r] = 1;
105 register struct ent ** pp = ATBL(tbl, r, 0);
106 for (c = 0; c++ <= maxcol; pp++)
107 if (*pp != NULL) {
108 //(*pp)->next = freeents; /* save [struct ent] for reuse */
109 //freeents = *pp;
110
111 clearent(*pp);
112 }
113 }
114
115 for (c = 0; c < COLFORMATS; c++) {
116 if (colformat[c] != NULL)
117 scxfree(colformat[c]);
118 colformat[c] = NULL;
119 }
120
121 maxrow = 0;
122 maxcol = 0;
123
124 clean_range();
125
126 calc_order = BYROWS;
127 prescale = 1.0;
128 tbl_style = 0;
129 optimize = 0;
130 currow = curcol = 0;
131
132 *curfile = '\0';
133 }
134
135 /**
136 * \brief load rc config file
137 *
138 * \return none
139 */
loadrc(void)140 void loadrc(void) {
141 char rcpath[PATHLEN];
142 char * home;
143
144 if ((home = getenv("XDG_CONFIG_HOME"))) {
145 char config_dir[PATHLEN-(sizeof CONFIG_FILE)];
146 snprintf(config_dir, PATHLEN-(sizeof CONFIG_FILE), "%s/sc-im", home);
147 mkdir(config_dir,0777);
148 snprintf(rcpath, PATHLEN, "%s/%s", config_dir, CONFIG_FILE);
149 (void) readfile(rcpath, 0);
150 }
151 /* Default to compile time if XDG_CONFIG_HOME not found */
152 else if ((home = getenv("HOME"))) {
153 char config_dir[PATHLEN];
154 sprintf(config_dir, "%s/%s", home,CONFIG_DIR);
155 mkdir(config_dir,0777);
156 snprintf(rcpath, PATHLEN, "%s/%s/%s", home,CONFIG_DIR,CONFIG_FILE);
157 (void) readfile(rcpath, 0);
158 }
159 *curfile = '\0';
160 }
161
162 /**
163 * \brief Check if a file exists
164 *
165 * \details Check if a file exists. Returns 1 if so. Returns 0 otherwise.
166 *
167 * \param[in] fname file name
168 *
169 * \return 1 if file exises; 0 otherwise
170 */
171
file_exists(const char * fname)172 int file_exists(const char * fname) {
173 FILE * file;
174 if ((file = fopen(fname, "r"))) {
175 fclose(file);
176 return 1;
177 }
178 return 0;
179 }
180
181 /**
182 * \brief Check if file has been modified since last save.
183 *
184 * \details This function checks if a file suffered mods since it was open.
185 *
186 * \return 0 if not modified; 1 if modified
187 */
modcheck()188 int modcheck() {
189 if (modflg && ! get_conf_int("nocurses")) {
190 sc_error("File not saved since last change. Add '!' to force");
191 return(1);
192 }
193 return 0;
194 }
195
196 /**
197 * \brief Return the proper delimiter for delimiter separated files.
198 *
199 * \details This function checks the type of a file as well as txtdelim conf value
200 *
201 * \return one of , ; \t |
202 */
get_delim(char * type)203 char get_delim(char *type) {
204 char delim = ',';
205 if (!strcasecmp(type, "tsv") || !strcasecmp(type, "tab"))
206 delim = '\t';
207
208 if (get_conf_value("txtdelim") != NULL) {
209 if (!strcasecmp(get_conf_value("txtdelim"), "\\t")) {
210 delim = '\t';
211 } else if (!strcasecmp(get_conf_value("txtdelim"), ",")) {
212 delim = ',';
213 } else if (!strcasecmp(get_conf_value("txtdelim"), ";")) {
214 delim = ';';
215 } else if (!strcasecmp(get_conf_value("txtdelim"), "|")) {
216 delim = '|';
217 }
218 }
219 return delim;
220 }
221
222 /**
223 * \brief TODO Handle the save file process
224 *
225 * This funciton handles the save file process in SC-IM format..
226 *
227 * \return 0 on OK; -1 on error
228 */
savefile()229 int savefile() {
230 int force_rewrite = 0;
231 char name[BUFFERSIZE];
232 #ifndef NO_WORDEXP
233 size_t len;
234 wordexp_t p;
235 #endif
236
237 if (! curfile[0] && wcslen(inputline) < 3) { // casos ":w" ":w!" ":x" ":x!"
238 sc_error("There is no filename");
239 return -1;
240 }
241
242 if (inputline[1] == L'!') force_rewrite = 1;
243
244 wcstombs(name, inputline, BUFFERSIZE);
245 del_range_chars(name, 0, 1 + force_rewrite);
246
247 #ifndef NO_WORDEXP
248 wordexp(name, &p, 0);
249 if (p.we_wordc < 1) {
250 sc_error("Failed expanding filepath");
251 return -1;
252 }
253 if ((len = strlen(p.we_wordv[0])) >= sizeof(name)) {
254 sc_error("File path too long");
255 wordfree(&p);
256 return -1;
257 }
258 memcpy(name, p.we_wordv[0], len+1);
259 wordfree(&p);
260 #endif
261
262 if (! force_rewrite && file_exists(name)) {
263 sc_error("File already exists. Use \"!\" to force rewrite.");
264 return -1;
265 }
266
267 #ifdef AUTOBACKUP
268 // check if backup of curfile exists.
269 // if it exists, remove it.
270 if (strlen(curfile) && backup_exists(curfile)) remove_backup(curfile);
271
272 // check if backup of newfilename exists.
273 // if it exists and '!' is set, remove it.
274 // if it exists and no '!' is set, return.
275 if (!strlen(curfile) && backup_exists(name)) {
276 if (!force_rewrite) {
277 sc_error("Backup file of %s exists. Use \"!\" to force the write process.", name);
278 return -1;
279 } else remove_backup(name);
280 }
281 #endif
282
283 // copy newfilename to curfile
284 if (wcslen(inputline) > 2) {
285 strcpy(curfile, name);
286 }
287
288 // add sc extension if not present
289 if (wcslen(inputline) > 2 && str_in_str(curfile, ".") == -1) {
290 sprintf(curfile + strlen(curfile), ".sc");
291
292 // treat csv
293 } else if (strlen(curfile) > 4 && (! strcasecmp( & curfile[strlen(curfile)-4], ".csv"))) {
294 export_delim(curfile, get_delim("csv"), 0, 0, maxrow, maxcol, 1);
295 modflg = 0;
296 return 0;
297
298 // treat tab
299 } else if (strlen(curfile) > 4 && (! strcasecmp( & curfile[strlen(curfile)-4], ".tsv") ||
300 ! strcasecmp( & curfile[strlen(curfile)-4], ".tab"))){
301 export_delim(curfile, '\t', 0, 0, maxrow, maxcol, 1);
302 modflg = 0;
303 return 0;
304
305 // treat markdown format
306 } else if (strlen(curfile) > 3 && ( ! strcasecmp( & curfile[strlen(curfile)-3], ".md") ||
307 ! strcasecmp( & curfile[strlen(curfile)-4], ".mkd"))){
308 export_markdown(curfile, 0, 0, maxrow, maxcol);
309 modflg = 0;
310 return 0;
311
312 // treat xlsx format
313 } else if (strlen(curfile) > 5 && ( ! strcasecmp( & curfile[strlen(curfile)-5], ".xlsx") ||
314 ! strcasecmp( & curfile[strlen(curfile)-5], ".xlsx"))){
315 #ifndef XLSX_EXPORT
316 sc_error("XLSX export support not compiled in. Please save file in other extension.");
317 return -1;
318 #else
319 if (export_xlsx(curfile, 0, 0, maxrow, maxcol) == 0) {
320 sc_info("File \"%s\" written", curfile);
321 modflg = 0;
322 } else
323 sc_error("File could not be saved");
324 return 0;
325 #endif
326 }
327
328 // save in sc format
329 if (writefile(curfile, 0, 0, maxrow, maxcol, 1) < 0) {
330 sc_error("File could not be saved");
331 return -1;
332 }
333 modflg = 0;
334 return 0;
335 }
336
337 /**
338 * \brief Write a file
339 *
340 * \details Write a file. Receives parameter range and file name.
341 *
342 * \param[in] fname file name
343 * \param[in] r0
344 * \param[in] c0
345 * \param[in] rn
346 * \param[in] cn
347 * \param[in] verbose
348 *
349 * \return 0 on success; -1 on error
350 */
writefile(char * fname,int r0,int c0,int rn,int cn,int verbose)351 int writefile(char * fname, int r0, int c0, int rn, int cn, int verbose) {
352 register FILE *f;
353 char save[PATHLEN];
354 char tfname[PATHLEN];
355 int pid;
356
357 (void) strcpy(tfname, fname);
358
359 (void) strcpy(save, tfname);
360
361 if ((f = openfile(tfname, &pid, NULL)) == NULL) {
362 if (verbose) sc_error("Can't create file \"%s\"", save);
363 return -1;
364 }
365
366 if (verbose) sc_info("Writing file \"%s\"...", save);
367 write_fd(f, r0, c0, rn, cn);
368
369 closefile(f, pid, 0);
370
371 if (! pid) {
372 (void) strcpy(curfile, save);
373 modflg = 0;
374 if (verbose) sc_info("File \"%s\" written", curfile);
375 }
376
377 return 0;
378 }
379
380 /**
381 * \brief TODO Document write_fd
382 *
383 * \param[in] f file pointer
384 * \param[in] r0
385 * \param[in] c0
386 * \param[in] rn
387 * \param[in] cn
388 *
389 * \return none
390 */
write_fd(register FILE * f,int r0,int c0,int rn,int cn)391 void write_fd(register FILE *f, int r0, int c0, int rn, int cn) {
392 register struct ent **pp;
393 int r, c;
394
395 (void) fprintf(f, "# This data file was generated by the Spreadsheet Calculator Improvised (SC-IM)\n");
396 (void) fprintf(f, "# You almost certainly shouldn't edit it.\n\n");
397 print_options(f);
398 for (c = 0; c < COLFORMATS; c++)
399 if (colformat[c])
400 (void) fprintf (f, "format %d = \"%s\"\n", c, colformat[c]);
401
402 for (c = c0; c <= cn; c++)
403 if (fwidth[c] != DEFWIDTH || precision[c] != DEFPREC || realfmt[c] != DEFREFMT)
404 (void) fprintf (f, "format %s %d %d %d\n", coltoa(c), fwidth[c], precision[c], realfmt[c]);
405
406 for (r = r0; r <= rn; r++)
407 if (row_format[r] != 1)
408 (void) fprintf (f, "format %d %d\n", r, row_format[r]);
409
410 // new implementation of hidecol. group by ranges
411 for (c = c0; c <= cn; c++) {
412 int c_aux = c;
413 if ( col_hidden[c] && c <= maxcol && ( c == 0 || !col_hidden[c-1] )) {
414 while (c_aux <= maxcol && col_hidden[c_aux])
415 c_aux++;
416 fprintf(f, "hidecol %s", coltoa(c));
417 if (c_aux-1 != c) {
418 fprintf(f, ":%s\n", coltoa(c_aux-1));
419 c = c_aux-1;
420 } else
421 fprintf(f, "\n");
422 }
423 }
424
425 // new implementation of hiderow. group by ranges
426 for (r = r0; r <= rn; r++) {
427 int r_aux = r;
428 if ( row_hidden[r] && r <= maxrow && ( r == 0 || !row_hidden[r-1] )) {
429 while (r_aux <= maxrow && row_hidden[r_aux])
430 r_aux++;
431 fprintf(f, "hiderow %d", r);
432 if (r_aux-1 != r) {
433 fprintf(f, ":%d\n", r_aux-1);
434 r = r_aux-1;
435 } else
436 fprintf(f, "\n");
437 }
438 }
439
440 // frozen cols. group by ranges
441 for (c = c0; c <= cn; c++) {
442 int c_aux = c;
443 if (col_frozen[c] && c <= maxcol && (c == 0 || ! col_frozen[c-1])) {
444 while (c_aux <= maxcol && col_frozen[c_aux]) c_aux++;
445 fprintf(f, "freeze %s", coltoa(c));
446 if (c_aux-1 != c) {
447 fprintf(f, ":%s\n", coltoa(c_aux-1));
448 c = c_aux-1;
449 } else
450 fprintf(f, "\n");
451 }
452 }
453
454 // frozen rows. group by ranges
455 for (r = r0; r <= rn; r++) {
456 int r_aux = r;
457 if (row_frozen[r] && r <= maxrow && (r == 0 || ! row_frozen[r-1])) {
458 while (r_aux <= maxrow && row_frozen[r_aux]) r_aux++;
459 fprintf(f, "freeze %d", r);
460 if (r_aux-1 != r) {
461 fprintf(f, ":%d\n", r_aux-1);
462 r = r_aux-1;
463 } else
464 fprintf(f, "\n");
465 }
466 }
467
468 write_marks(f);
469 write_franges(f);
470
471 write_cells(f, r0, c0, rn, cn, r0, c0);
472
473 struct custom_color * cc;
474 for (r = r0; r <= rn; r++) {
475 pp = ATBL(tbl, r, c0);
476 for (c = c0; c <= cn; c++, pp++)
477 if (*pp) {
478 // Write ucolors
479 if ((*pp)->ucolor != NULL) {
480 char strcolorbuf[BUFFERSIZE];
481 char * strcolor = strcolorbuf;
482 strcolor[0] = 0;
483 strcolor[1] = 0;
484
485 // decompile int value of color to its string description
486 if ((*pp)->ucolor->fg != NONE_COLOR) {
487 if ((*pp)->ucolor->fg <= 8) {
488 linelim=0;
489 struct enode * e = new((*pp)->ucolor->fg, (struct enode *)0, (struct enode *)0);
490 decompile(e, 0);
491 uppercase(line);
492 del_char(line, 0);
493 sprintf(strcolor, " fg=%.*s", BUFFERSIZE-5, &line[0]);
494 free(e);
495 } else if ((cc = get_custom_color_by_number((*pp)->ucolor->fg - 7)) != NULL) {
496 sprintf(strcolor, " fg=%.*s", BUFFERSIZE, cc->name);
497 }
498 }
499
500 if ((*pp)->ucolor->bg != NONE_COLOR) {
501 if ((*pp)->ucolor->bg <= WHITE) {
502 linelim=0;
503 struct enode * e = new((*pp)->ucolor->bg, (struct enode *)0, (struct enode *)0);
504 decompile(e, 0);
505 uppercase(line);
506 del_char(line, 0);
507 sprintf(strcolor + strlen(strcolor), " bg=%s", &line[0]);
508 free(e);
509 } else if ((cc = get_custom_color_by_number((*pp)->ucolor->bg - 7)) != NULL) {
510 sprintf(strcolor + strlen(strcolor), " bg=%.*s", BUFFERSIZE, cc->name);
511 }
512 }
513
514 if ((*pp)->ucolor->bold) sprintf(strcolor + strlen(strcolor), " bold=1");
515 if ((*pp)->ucolor->italic) sprintf(strcolor + strlen(strcolor), " italic=1");
516 if ((*pp)->ucolor->dim) sprintf(strcolor + strlen(strcolor), " dim=1");
517 if ((*pp)->ucolor->reverse) sprintf(strcolor + strlen(strcolor), " reverse=1");
518 if ((*pp)->ucolor->standout) sprintf(strcolor + strlen(strcolor), " standout=1");
519 if ((*pp)->ucolor->underline) sprintf(strcolor + strlen(strcolor), " underline=1");
520 if ((*pp)->ucolor->blink) sprintf(strcolor + strlen(strcolor), " blink=1");
521
522 // Remove the leading space
523 strcolor++;
524
525 // previous implementation
526 //(void) fprintf(f, "cellcolor %s%d \"%s\"\n", coltoa((*pp)->col), (*pp)->row, strcolor);
527
528 // new implementation
529 // by row, store cellcolors grouped by ranges
530 int c_aux = c;
531 struct ucolor * u = (*pp)->ucolor;
532 struct ucolor * a = NULL;
533 if ( c > 0 && *ATBL(tbl, r, c-1) != NULL)
534 a = (*ATBL(tbl, r, c-1))->ucolor;
535
536 if ( *strcolor != '\0' && (u != NULL) && (c <= maxcol) && ( c == 0 || ( a == NULL ) || ( a != NULL && ! same_ucolor( a, u ) ))) {
537 while (c_aux <= maxcol && *ATBL(tbl, r, c_aux) != NULL && same_ucolor( (*ATBL(tbl, r, c_aux))->ucolor, (*pp)->ucolor ))
538 c_aux++;
539 fprintf(f, "cellcolor %s%d", coltoa((*pp)->col), (*pp)->row);
540 if (c_aux-1 != (*pp)->col)
541 fprintf(f, ":%s%d \"%s\"\n", coltoa(c_aux-1), (*pp)->row, strcolor);
542 else
543 fprintf(f, " \"%s\"\n", strcolor);
544 }
545
546 }
547
548
549 /* if ((*pp)->nrow >= 0) {
550 (void) fprintf(f, "addnote %s ", v_name((*pp)->row, (*pp)->col));
551 (void) fprintf(f, "%s\n", r_name((*pp)->nrow, (*pp)->ncol, (*pp)->nlastrow, (*pp)->nlastcol));
552 } */
553
554 // padding
555 // previous implementation
556 //if ((*pp)->pad)
557 // (void) fprintf(f, "pad %d %s%d\n", (*pp)->pad, coltoa((*pp)->col), (*pp)->row);
558 // new implementation
559 int r_aux = r;
560 if ( (*pp)->pad && r <= maxrow && ( r == 0 || (*ATBL(tbl, r-1, c) == NULL) ||
561 (*ATBL(tbl, r-1, c) != NULL && ((*ATBL(tbl, r-1, c))->pad != (*pp)->pad)) )) {
562 while (r_aux <= maxrow && *ATBL(tbl, r_aux, c) != NULL && (*pp)->pad == (*ATBL(tbl, r_aux, c))->pad )
563 r_aux++;
564 fprintf(f, "pad %d %s%d", (*pp)->pad, coltoa((*pp)->col), (*pp)->row);
565 if (r_aux-1 != (*pp)->row)
566 fprintf(f, ":%s%d\n", coltoa((*pp)->col), r_aux-1);
567 else
568 fprintf(f, "\n");
569 }
570 }
571 }
572
573 // write locked cells
574 // lock should be stored after any other command
575 for (r = r0; r <= rn; r++) {
576 pp = ATBL(tbl, r, c0);
577 for (c = c0; c <= cn; c++, pp++)
578 if (*pp) {
579 // previous implementation
580 //if ((*pp)->flags & is_locked)
581 // (void) fprintf(f, "lock %s%d\n", coltoa((*pp)->col), (*pp)->row);
582 // new implementation
583 int c_aux = c;
584 if ( (*pp)->flags & is_locked && c <= maxcol && ( c == 0 || ( *ATBL(tbl, r, c-1) != NULL && ! ((*ATBL(tbl, r, c-1))->flags & is_locked) ) )) {
585 while (c_aux <= maxcol && *ATBL(tbl, r, c_aux) != NULL && (*ATBL(tbl, r, c_aux))->flags & is_locked )
586 c_aux++;
587 fprintf(f, "lock %s%d", coltoa((*pp)->col), (*pp)->row);
588 if (c_aux-1 != (*pp)->col)
589 fprintf(f, ":%s%d\n", coltoa(c_aux-1), (*pp)->row);
590 else
591 fprintf(f, "\n");
592 }
593 }
594 }
595
596 /*
597 * Don't try to combine these into a single fprintf(). v_name() has
598 * a single buffer that is overwritten on each call, so the first part
599 * needs to be written to the file before making the second call.
600 */
601 fprintf(f, "goto %s", v_name(currow, curcol));
602 //fprintf(f, " %s\n", v_name(strow, stcol));
603 fprintf(f, "\n");
604 }
605
606 /**
607 * \brief TODO Document write_franges()
608 *
609 * \param[in] f file pointer
610 *
611 * \return none
612 */
write_franges(register FILE * f)613 void write_franges(register FILE *f) {
614 if (! freeze_ranges) return;
615 if (freeze_ranges->type == 'a') {
616 fprintf(f, "freeze %s%d", coltoa(freeze_ranges->tl->col), freeze_ranges->tl->row);
617 fprintf(f, ":%s%d\n", coltoa(freeze_ranges->br->col), freeze_ranges->br->row);
618 } else if (freeze_ranges->type == 'c' && freeze_ranges->tl->col == freeze_ranges->br->col) {
619 fprintf(f, "freeze %s\n", coltoa(freeze_ranges->tl->col));
620 } else if (freeze_ranges->type == 'c') {
621 fprintf(f, "freeze %s:", coltoa(freeze_ranges->tl->col));
622 fprintf(f, "%s\n", coltoa(freeze_ranges->br->col));
623 } else if (freeze_ranges->type == 'r' && freeze_ranges->tl->row == freeze_ranges->br->row) {
624 fprintf(f, "freeze %d\n", freeze_ranges->tl->row);
625 } else if (freeze_ranges->type == 'r') {
626 fprintf(f, "freeze %d:%d\n", freeze_ranges->tl->row, freeze_ranges->br->row);
627 }
628 }
629
630 /**
631 * \brief TODO Document write_marks()
632 *
633 * \param[in] f file pointer
634 *
635 * \return none
636 */
write_marks(register FILE * f)637 void write_marks(register FILE *f) {
638 int i;
639 struct mark * m;
640
641 for ( i='a'; i<='z'; i++ ) {
642 m = get_mark((char) i);
643
644 // m->rng should never be NULL if both m->col and m->row are -1 !!
645 if ( m->row == -1 && m->col == -1) { // && m->rng != NULL ) {
646 fprintf(f, "mark %c %s%d ", i, coltoa(m->rng->tlcol), m->rng->tlrow);
647 fprintf(f, "%s%d\n", coltoa(m->rng->brcol), m->rng->brrow);
648 } else if ( m->row != 0 && m->row != 0) { // && m->rng == NULL) {
649 fprintf(f, "mark %c %s%d\n", i, coltoa(m->col), m->row);
650 }
651 }
652
653 return;
654 }
655
656 /**
657 * \brief TODO Document write_cells()
658 *
659 * \param[in] f file pointer
660 * \param[in] r0
661 * \param[in] c0
662 * \param[in] rn
663 * \param[in] cn
664 * \param[in] dr
665 * \param[in[ dc
666 *
667 * \return none
668 */
write_cells(register FILE * f,int r0,int c0,int rn,int cn,int dr,int dc)669 void write_cells(register FILE *f, int r0, int c0, int rn, int cn, int dr, int dc) {
670 register struct ent **pp;
671 int r, c;
672 //int r, c, mf;
673 char *dpointptr;
674
675 //mf = modflg;
676 if (dr != r0 || dc != c0) {
677 //yank_area(r0, c0, rn, cn);
678 rn += dr - r0;
679 cn += dc - c0;
680 //rs = currow;
681 //cs = curcol;
682 currow = dr;
683 curcol = dc;
684 }
685 //if (Vopt) valueize_area(dr, dc, rn, cn);
686 for (r = dr; r <= rn; r++) {
687 pp = ATBL(tbl, r, dc);
688 for (c = dc; c <= cn; c++, pp++)
689 if (*pp) {
690 if ((*pp)->label || (*pp)->flags & is_strexpr) {
691 edits(r, c, 1);
692 (void) fprintf(f, "%s\n", line);
693 }
694 if ((*pp)->flags & is_valid) {
695 //if ((*pp)->flags & is_valid || (*pp)->expr) { // for #541
696 editv(r, c);
697 dpointptr = strchr(line, dpoint);
698 if (dpointptr != NULL)
699 *dpointptr = '.';
700 (void) fprintf(f, "%s\n", line);
701 }
702 if ((*pp)->format) {
703 editfmt(r, c);
704 (void) fprintf(f, "%s\n",line);
705 }
706 }
707 }
708 //modflg = mf;
709 }
710
711 /**
712 * \brief Try to open a spreadsheet file.
713 *
714 * \param[in] fname file name
715 * \param[in] eraseflg
716 *
717 * \return SC_READFILE_SUCCESS if we loaded the file, SC_READFILE_ERROR if we failed,
718 * SC_READFILE_DOESNTEXIST if the file doesn't exist.
719 */
readfile(char * fname,int eraseflg)720 sc_readfile_result readfile(char * fname, int eraseflg) {
721 if (!strlen(fname)) return 0;
722 loading = 1;
723
724 #ifdef AUTOBACKUP
725 // Check if curfile is set and backup exists..
726 if (str_in_str(fname, CONFIG_FILE) == -1 && strlen(curfile) &&
727 backup_exists(curfile) && strcmp(fname, curfile)) {
728 if (modflg) {
729 // TODO - force load with '!' ??
730 sc_error("There are changes unsaved. Cannot load file: %s", fname);
731 loading = 0;
732 return SC_READFILE_ERROR;
733 }
734 remove_backup(curfile);
735 }
736 // Check if fname is set and backup exists..
737 if (backup_exists(fname)) {
738 wchar_t msg[BUFFERSIZE];
739 swprintf(msg, BUFFERSIZE,
740 // TODO - Open backup readonly ??
741 L"Backup of %s file exists. Do you want to (E)dit the file and remove the backup, (R)ecover the backup or (Q)uit: ", fname);
742 wchar_t t = ui_query_opt(msg, L"qerQER");
743 switch (t) {
744 case L'q':
745 case L'Q':
746 loading = 0;
747 extern int shall_quit;
748 shall_quit = 1;
749 return SC_READFILE_ERROR;
750 break;
751 case L'e':
752 case L'E':
753 remove_backup(fname);
754 break;
755 case L'r':
756 case L'R':
757 ;
758 int len = strlen(fname);
759 if (!len) return 0;
760 char * pstr = strrchr(fname, '/');
761 int pos = pstr == NULL ? -1 : pstr - fname;
762 char bkpname[len+6];
763 strcpy(bkpname, fname);
764 add_char(bkpname, '.', pos+1);
765 sprintf(bkpname + strlen(bkpname), ".bak");
766 remove(fname);
767 rename(bkpname, fname);
768 break;
769 }
770 }
771 #endif
772
773 // Check if file is a correct format
774 int len = strlen(fname);
775 if (! strcmp( & fname[len-3], ".sc") ||
776 (len >= strlen(CONFIG_FILE) && ! strcasecmp( & fname[len-strlen(CONFIG_FILE)], CONFIG_FILE))) {
777 // pass
778
779 // If file is an xlsx file, we import it
780 } else if (len > 5 && ! strcasecmp( & fname[len-5], ".xlsx")){
781 #ifndef XLSX
782 sc_error("XLSX import support not compiled in");
783 #else
784 open_xlsx(fname, "UTF-8");
785 strcpy(curfile, fname);
786 modflg = 0;
787 #endif
788 loading = 0;
789 return SC_READFILE_SUCCESS;
790
791 // If file is an ODS file, we import it
792 } else if (len > 4 && ! strcasecmp( & fname[len-4], ".ods")){
793 #ifndef ODS
794 sc_error("ODS import support not compiled in");
795 #else
796 open_ods(fname, "UTF-8");
797 strcpy(curfile, fname);
798 modflg = 0;
799 #endif
800 loading = 0;
801 return SC_READFILE_SUCCESS;
802
803 // If file is an xls file, we import it
804 } else if (len > 4 && ! strcasecmp( & fname[len-4], ".xls")){
805 #ifndef XLS
806 sc_error("XLS import support not compiled in");
807 #else
808 open_xls(fname, "UTF-8");
809 modflg = 0;
810 strcpy(curfile, fname);
811 #endif
812 loading = 0;
813 return SC_READFILE_SUCCESS;
814
815 // If file is an delimited text file, we import it
816 } else if (len > 4 && ( ! strcasecmp( & fname[len-4], ".csv") ||
817 ! strcasecmp( & fname[len-4], ".tsv") || ! strcasecmp( & fname[len-4], ".tab") ||
818 ! strcasecmp( & fname[len-4], ".txt") )){
819
820 import_csv(fname, get_delim(&fname[len-3])); // csv tsv tab txt delim import
821 strcpy(curfile, fname);
822 modflg = 0;
823 loading = 0;
824 return SC_READFILE_SUCCESS;
825
826 // If file is a markdown text file, we try to import it
827 } else if (len > 3 && ( ! strcasecmp( & fname[len-3], ".md") ||
828 ! strcasecmp( & fname[len-4], ".mkd"))){
829
830 import_markdown(fname);
831 strcpy(curfile, fname);
832 modflg = 0;
833 loading = 0;
834 return SC_READFILE_SUCCESS;
835
836 } else {
837 sc_info("\"%s\" is not a SC-IM compatible file", fname);
838 loading = 0;
839 return SC_READFILE_ERROR;
840 }
841
842 // We open an 'sc' format file
843 // open fname for reading
844 register FILE * f;
845 char save[PATHLEN];
846 if (*fname == '\0') fname = curfile;
847 (void) strcpy(save, fname);
848 f = fopen(save, "r");
849 if (f == NULL) {
850 loading = 0;
851 strcpy(curfile, save);
852 return SC_READFILE_DOESNTEXIST;
853 } /* */
854
855 if (eraseflg) erasedb();
856
857 while (! brokenpipe && fgets(line, sizeof(line), f)) {
858 linelim = 0;
859 if (line[0] != '#') (void) yyparse();
860 }
861 fclose(f);
862
863 loading = 0;
864 linelim = -1;
865 if (eraseflg) {
866 cellassign = 0;
867 }
868 strcpy(curfile, save);
869 EvalAll();
870 modflg = 0;
871 return SC_READFILE_SUCCESS;
872 }
873
874 /**
875 * \brief Expand a ~ in path to the user's home directory
876 *
877 * \param[in] path
878 *
879 * \return path
880 */
findhome(char * path)881 char * findhome(char * path) {
882 static char * HomeDir = NULL;
883
884 if (* path == '~') {
885 char * pathptr;
886 char tmppath[PATHLEN];
887
888 if (HomeDir == NULL) {
889 HomeDir = getenv("HOME");
890 if (HomeDir == NULL)
891 HomeDir = "/";
892 }
893 pathptr = path + 1;
894 if ((* pathptr == '/') || (* pathptr == '\0'))
895 strcpy(tmppath, HomeDir);
896 else {
897 struct passwd * pwent;
898 char * namep;
899 char name[50];
900
901 namep = name;
902 while ((*pathptr != '\0') && (*pathptr != '/'))
903 *(namep++) = *(pathptr++);
904 *namep = '\0';
905 if ((pwent = getpwnam(name)) == NULL) {
906 (void) sprintf(path, "Can't find user %s", name);
907 return (NULL);
908 }
909 strcpy(tmppath, pwent->pw_dir);
910 }
911 strcat(tmppath, pathptr);
912 strcpy(path, tmppath);
913 }
914 return (path);
915 }
916
917 /**
918 * \brief Open the input or output file
919 *
920 * \details Open the input or output file, setting up a pipe if needed.
921 *
922 * \param[in] fname file name
923 * \param[in] rpid
924 * \param[in] rfd
925 *
926 * \return file pointer
927 */
openfile(char * fname,int * rpid,int * rfd)928 FILE * openfile(char *fname, int *rpid, int *rfd) {
929 int pipefd[4];
930 int pid;
931 FILE *f;
932 char *efname;
933
934 while (*fname && (*fname == ' ')) // Skip leading blanks
935 fname++;
936
937 if (*fname != '|') { // Open file if not pipe
938 *rpid = 0;
939 if (rfd != NULL)
940 *rfd = 1; // Set to stdout just in case
941
942 efname = findhome(fname);
943 return (fopen(efname, rfd == NULL ? "w" : "r"));
944 }
945
946 fname++; // Skip |
947 efname = findhome(fname);
948 if (pipe(pipefd) < 0 || (rfd != NULL && pipe(pipefd+2) < 0)) {
949 sc_error("Can't make pipe to child");
950 *rpid = 0;
951 return (0);
952 }
953
954 //deraw(rfd==NULL);
955
956 if ((pid=fork()) == 0) { // if child
957 (void) close(0); // close stdin
958 (void) close(pipefd[1]);
959 (void) dup(pipefd[0]); // connect to first pipe
960 if (rfd != NULL) { // if opening for read
961 (void) close(1); // close stdout
962 (void) close(pipefd[2]);
963 (void) dup(pipefd[3]); // connect to second pipe
964 }
965 (void) signal(SIGINT, SIG_DFL); // reset
966 execl("/bin/sh", "sh", "-c", efname, 0, (char *) NULL);
967 exit (-127);
968 } else { // else parent
969 *rpid = pid;
970 if ((f = fdopen(pipefd[(rfd==NULL?1:2)], rfd==NULL?"w":"r")) == NULL) {
971 (void) kill(pid, 9);
972 sc_error("Can't fdopen %sput", rfd==NULL?"out":"in");
973 (void) close(pipefd[1]);
974 if (rfd != NULL)
975 (void) close(pipefd[3]);
976 *rpid = 0;
977 return (0);
978 }
979 }
980 (void) close(pipefd[0]);
981 if (rfd != NULL) {
982 (void) close(pipefd[3]);
983 *rfd = pipefd[1];
984 }
985 return (f);
986 }
987
988 // close a file opened by openfile(), if process wait for return
989 /**
990 * \brief Close a file opened by openfile()
991 *
992 * \details Close a file opened by openfile(). If process, wait for return
993 *
994 * \param[in] f file pointer
995 * \param[in] pid
996 * \param[in] rfd
997 *
998 * \return none
999 */
closefile(FILE * f,int pid,int rfd)1000 void closefile(FILE *f, int pid, int rfd) {
1001 int temp;
1002 wint_t wi;
1003
1004 (void) fclose(f);
1005 if (pid) {
1006 while (pid != wait(&temp)) //;
1007 if (rfd==0) {
1008 printf("Press any key to continue ");
1009 fflush(stdout);
1010 #ifdef NCURSES
1011 cbreak();
1012 #endif
1013 ui_getch_b(&wi);
1014 } else {
1015 close(rfd);
1016 #ifdef NCURSES
1017 if (! get_conf_int("nocurses")) {
1018 cbreak();
1019 nonl();
1020 noecho ();
1021 }
1022 #endif
1023 }
1024 }
1025 if (brokenpipe) {
1026 sc_error("Broken pipe");
1027 brokenpipe = FALSE;
1028 }
1029 }
1030
1031 /**
1032 * \brief TODO <brief function description>
1033 *
1034 * \param[in] f file pointer
1035 *
1036 * \return none
1037 */
print_options(FILE * f)1038 void print_options(FILE *f) {
1039 if (
1040 ! optimize &&
1041 ! rndtoeven &&
1042 calc_order == BYROWS &&
1043 prescale == 1.0 &&
1044 ! get_conf_int("external_functions") &&
1045 tbl_style == 0
1046 )
1047 return; // No reason to do this
1048
1049 (void) fprintf(f, "set");
1050 if (optimize) (void) fprintf(f," optimize");
1051 if (rndtoeven) (void) fprintf(f, " rndtoeven");
1052 if (calc_order != BYROWS ) (void) fprintf(f, " bycols");
1053 if (prescale != 1.0) (void) fprintf(f, " prescale");
1054 if ( get_conf_int("external_functions") ) (void) fprintf(f, " external_functions");
1055 if (tbl_style) (void) fprintf(f, " tblstyle = %s", tbl_style == TBL ? "tbl" : tbl_style == LATEX ? "latex" : tbl_style == SLATEX ? "slatex" : tbl_style == TEX ? "tex" : tbl_style == FRAME ? "frame" : "0" );
1056 (void) fprintf(f, "\n");
1057 }
1058
1059
1060 /**
1061 * \brief Import csv to sc
1062 *
1063 * \param[in] fname file name
1064 * \param[in] d = delim character
1065 *
1066 * \return 0 on success; -1 on error
1067 */
import_csv(char * fname,char d)1068 int import_csv(char * fname, char d) {
1069 register FILE * f;
1070 int r = 0, c = 0, cf = 0;
1071 wchar_t line_interp[FBUFLEN] = L"";
1072 char * token;
1073
1074 int quote = 0; // if value has '"'. ex: 12,"1234,450.00",56
1075 char delim[2] = ""; //strtok receives a char *, not a char
1076 add_char(delim, d, 0);
1077
1078 if ((f = fopen(fname , "r")) == NULL) {
1079 sc_error("Can't read file \"%s\"", fname);
1080 return -1;
1081 }
1082
1083 // Check max length of line
1084 int max = max_length(f) + 1;
1085 if (max == 0) {
1086 sc_error("Can't read file \"%s\"", fname);
1087 return -1;
1088 }
1089 char line_in[max];
1090 rewind(f);
1091
1092 // Check the numbers of lines in file
1093 int max_lines = count_lines(f);
1094 rewind(f);
1095
1096 int i=0;
1097
1098 // handle ","
1099 char lookf[4], repls[2], replb[2];
1100 sprintf(lookf, "\"%c\"", d);
1101 sprintf(repls, "%c", 6);
1102 sprintf(replb, "%c", d);
1103
1104 // CSV file traversing
1105 while ( ! feof(f) && (fgets(line_in, sizeof(line_in), f) != NULL) ) {
1106 // show file loading progress
1107 i++; // increase number of line;
1108 if (i % 10 == 0 ) sc_info("loading line %d of %d", i, max_lines);
1109
1110 // this hack is for importing file that have DOS eol
1111 int l = strlen(line_in);
1112 while (l--)
1113 if (line_in[l] == 0x0d) {
1114 line_in[l] = '\0';
1115 break;
1116 }
1117
1118 strcpy(line_in, str_replace(line_in, lookf, repls)); // handle "," case
1119
1120 // Split string using the delimiter
1121 token = xstrtok(line_in, delim);
1122 c = 0;
1123
1124 while( token != NULL ) {
1125 if (r > MAXROWS - GROWAMT - 1 || c > ABSMAXCOLS - 1) break;
1126 clean_carrier(token);
1127 if ( token[0] == '\"' && token[strlen(token)-1] == '\"') {
1128 quote = 1;
1129 } else if ( (token[0] == '\"' || quote) && strlen(token) && (token[strlen(token)-1] != '\"' || strlen(token) == 1) ) {
1130 quote = 1;
1131 char * next = xstrtok(NULL, delim);
1132
1133 if (next != NULL) {
1134 sprintf(token + strlen(token), "%c%s", d, next);
1135 continue;
1136 }
1137 }
1138 if (quote) { // Remove quotes
1139 del_char(token, 0);
1140 del_char(token, strlen(token)-1);
1141 }
1142
1143 char * st = str_replace (token, repls, replb); // handle "," case
1144
1145 // number import
1146 if (strlen(st) && isnumeric(st) && ! get_conf_int("import_delimited_as_text")
1147 ) {
1148 //wide char
1149 swprintf(line_interp, BUFFERSIZE, L"let %s%d=%s", coltoa(c), r, st);
1150
1151 // text import
1152 } else if (strlen(st)){
1153 //wide char
1154 swprintf(line_interp, BUFFERSIZE, L"label %s%d=\"%s\"", coltoa(c), r, st);
1155 }
1156 //wide char
1157 if (strlen(st)) send_to_interp(line_interp);
1158
1159 if (++c > cf) cf = c;
1160 quote = 0;
1161 token = xstrtok(NULL, delim);
1162 free(st);
1163 }
1164
1165 r++;
1166 if (r > MAXROWS - GROWAMT - 1 || c > ABSMAXCOLS - 1) break;
1167 }
1168 maxrow = r-1;
1169 maxcol = cf-1;
1170
1171 auto_justify(0, maxcols, DEFWIDTH);
1172
1173 fclose(f);
1174
1175 EvalAll();
1176 return 0;
1177 }
1178
1179 /**
1180 * \brief Import Markdown to sc
1181 *
1182 * \param[in] fname file name
1183 * \param[in] d
1184 *
1185 * \return 0 on success; -1 on error
1186 */
1187
import_markdown(char * fname)1188 int import_markdown(char * fname) {
1189 register FILE * f;
1190 int r = 0, c = 0, cf = 0;
1191 wchar_t line_interp[FBUFLEN] = L"";
1192 wchar_t line_interp_align[FBUFLEN] = L"";
1193 char * token;
1194
1195 //int pipe = 0; // if value has '"'. ex: 12,"1234,450.00",56
1196 int rownr = 0;
1197 char d = '|';
1198 char delim[2] = ""; //strtok receives a char *, not a char
1199 add_char(delim, d, 0);
1200 // int linenumber = 0;
1201
1202 if ((f = fopen(fname , "r")) == NULL) {
1203 sc_error("Can't read file \"%s\"", fname);
1204 return -1;
1205 }
1206
1207 // Check max length of line
1208 int max = max_length(f) + 1;
1209 if (max == 0) {
1210 sc_error("Can't read file \"%s\"", fname);
1211 return -1;
1212 }
1213 char line_in[max];
1214 char line_in_head[max];
1215 char align[max];
1216 rewind(f);
1217
1218 while ( ! feof(f) && (fgets(line_in, sizeof(line_in), f) != NULL) ) {
1219
1220 // this hack is for importing file that have DOS eol
1221 int l = strlen(line_in);
1222 while (l--){
1223 if (line_in[l] == 0x0d) {
1224 line_in[l] = '\0';
1225 break;
1226 }
1227 }
1228
1229 /*
1230 if ( line_in[0] == '|' && line_in[strlen(line_in)-1] == '|') {
1231 pipe = 1;
1232 }
1233 */
1234 //pipe = 0;
1235 del_char(line_in, 0);
1236 del_char(line_in, strlen(line_in)-1);
1237
1238 if(r==1){
1239 strcpy(line_in_head, line_in);
1240
1241 token = xstrtok(line_in_head, delim);
1242 c = 0;
1243
1244 while( token != NULL ) {
1245 if (r > MAXROWS - GROWAMT - 1 || c > ABSMAXCOLS - 1) break;
1246 clean_carrier(token);
1247 token = ltrim(token, ' ');
1248 token = rtrim(token, ' ');
1249
1250 if((token[0] == ':' && token[strlen(token)-1] == '-') ||
1251 (token[0] == '-' && token[strlen(token)-1] == '-')){
1252 align[c] = 'l';
1253 swprintf(line_interp_align, BUFFERSIZE, L"leftjustify %s", v_name(r-1, c));
1254
1255 }
1256 else if(token[0] == '-' && token[strlen(token)-1] == ':'){
1257 align[c] = 'r';
1258 swprintf(line_interp_align, BUFFERSIZE, L"rightjustify %s", v_name(r-1, c));
1259 }
1260 else{
1261 swprintf(line_interp_align, BUFFERSIZE, L"center %s", v_name(r-1, c));
1262 align[c] = 'c';
1263 }
1264
1265 send_to_interp(line_interp_align);
1266 token = xstrtok(NULL, delim);
1267 c++;
1268 }
1269 }
1270 else{
1271
1272 // Split string using the delimiter
1273 token = xstrtok(line_in, delim);
1274
1275 c = 0;
1276
1277 while( token != NULL ) {
1278 if (r > MAXROWS - GROWAMT - 1 || c > ABSMAXCOLS - 1) break;
1279
1280 if(r == 0){
1281 rownr = r;
1282 }
1283 else{
1284 rownr = r-1;
1285 }
1286
1287 clean_carrier(token);
1288 token = ltrim(token, ' ');
1289 token = rtrim(token, ' ');
1290
1291 char * st = str_replace(token, "\"", "''"); //replace double quotes inside string
1292
1293 // number import
1294 if (isnumeric(st) && strlen(st) && ! atoi(get_conf_value("import_delimited_as_text"))) {
1295 //wide char
1296 swprintf(line_interp, BUFFERSIZE, L"let %s%d=%s", coltoa(c), rownr, st);
1297
1298 // text import
1299 } else if (strlen(st)){
1300 //wide char
1301 swprintf(line_interp, BUFFERSIZE, L"label %s%d=\"%s\"", coltoa(c), rownr, st);
1302 }
1303 //wide char
1304 if (strlen(st)){
1305 send_to_interp(line_interp);
1306
1307 if(r>0){
1308 if(align[c] == 'l'){
1309 swprintf(line_interp_align, BUFFERSIZE, L"leftjustify %s", v_name(rownr, c));
1310 }
1311 else if(align[c] == 'r'){
1312 swprintf(line_interp_align, BUFFERSIZE, L"rightjustify %s", v_name(rownr, c));
1313 }
1314 else{
1315 swprintf(line_interp_align, BUFFERSIZE, L"center %s", v_name(rownr, c));
1316 }
1317 send_to_interp(line_interp_align);
1318 }
1319
1320 }
1321 free(st);
1322
1323 if (++c > cf) cf = c;
1324 token = xstrtok(NULL, delim);
1325 }
1326 }
1327
1328 maxcol = cf-1;
1329 r++;
1330 if (r > MAXROWS - GROWAMT - 1 || c > ABSMAXCOLS - 1) break;
1331 }
1332 maxrow = r-1;
1333 maxcol = cf-1;
1334
1335 auto_justify(0, maxcols, DEFWIDTH);
1336
1337 fclose(f);
1338
1339 EvalAll();
1340 return 0;
1341 }
1342
1343
1344
1345 /**
1346 * \brief Export to CSV, TAB, or plain TXT
1347 *
1348 * \param[in] r0
1349 * \param[in] c0
1350 * \param[in] rn
1351 * \param[in] cn
1352 *
1353 * \return none
1354 */
do_export(int r0,int c0,int rn,int cn)1355 void do_export(int r0, int c0, int rn, int cn) {
1356 int force_rewrite = 0;
1357 char type_export[4] = "";
1358 char ruta[PATHLEN];
1359 char linea[BUFFERSIZE];
1360
1361 if (inputline[1] == L'!') force_rewrite = 1;
1362 wcstombs(linea, inputline, BUFFERSIZE); // Use new variable to keep command history untouched
1363 del_range_chars(linea, 0, 1 + force_rewrite); // Remove 'e' or 'e!' from inputline
1364
1365 // Get format to export
1366 if (str_in_str(linea, "csv") == 0) {
1367 strcpy(type_export, "csv");
1368 } else if (str_in_str(linea, "tab") == 0) {
1369 strcpy(type_export, "tab");
1370 } else if (str_in_str(linea, "tex") == 0) {
1371 strcpy(type_export, "tex");
1372 } else if (str_in_str(linea, "txt") == 0) {
1373 strcpy(type_export, "txt");
1374 } else if (str_in_str(linea, "mkd") == 0) {
1375 strcpy(type_export, "mkd");
1376 }
1377
1378 // Get route and file name to write.
1379 // Use parameter if any.
1380 if (strlen(linea) > 4) { // ex. 'csv '
1381 del_range_chars(linea, 0, 3); // remove 'csv'
1382 strcpy(ruta, linea);
1383
1384 // Use curfile name and '.csv' o '.tab' extension
1385 // Remove current '.sc' extension if necessary
1386 } else if (curfile[0]) {
1387 strcpy(ruta, curfile);
1388 char * ext = strrchr(ruta, '.');
1389 if (ext != NULL) del_range_chars(ruta, strlen(ruta) - strlen(ext), strlen(ruta)-1);
1390 sprintf(ruta + strlen(ruta), ".%s", type_export);
1391
1392 } else {
1393 sc_error("No filename specified !");
1394 return;
1395 }
1396
1397 if (! force_rewrite && file_exists(ruta) && strlen(ruta) > 0) {
1398 sc_error("File %s already exists. Use \"!\" to force rewrite.", ruta);
1399 return;
1400 }
1401
1402 #ifdef AUTOBACKUP
1403 // check if backup of fname exists.
1404 // if it exists and '!' is set, remove it.
1405 // if it exists and curfile = fname, remove it.
1406 // else return.
1407 if (( !strcmp(type_export, "csv") || !strcmp(type_export, "tab")) && (strlen(ruta) && backup_exists(ruta))) {
1408 if (force_rewrite || (strlen(curfile) && !strcmp(curfile, ruta))) {
1409 remove_backup(ruta);
1410 } else {
1411 sc_error("Backup file of %s exists. Use \"!\" to force the write process.", ruta);
1412 return;
1413 }
1414 }
1415 #endif
1416
1417 // Call export routines
1418 if (strcmp(type_export, "csv") == 0) {
1419 export_delim(ruta, get_delim("csv"), r0, c0, rn, cn, 1);
1420 } else if (strcmp(type_export, "tab") == 0) {
1421 export_delim(ruta, '\t', r0, c0, rn, cn, 1);
1422 } else if (strcmp(type_export, "tex") == 0) {
1423 export_latex(ruta, r0, c0, rn, cn, 1);
1424 } else if (strcmp(type_export, "txt") == 0) {
1425 export_plain(ruta, r0, c0, rn, cn);
1426 } else if (strcmp(type_export, "mkd") == 0) {
1427 export_markdown(ruta, r0, c0, rn, cn);
1428 }
1429 }
1430
1431
1432 /**
1433 * \brief Export to md file with markdown table
1434 *
1435 * \param[in] fname file name
1436 * \param[in] r0
1437 * \param[in] c0
1438 * \param[in] rn
1439 * \param[in] cn
1440 *
1441 * \return none
1442 */
export_markdown(char * fname,int r0,int c0,int rn,int cn)1443 void export_markdown(char * fname, int r0, int c0, int rn, int cn) {
1444 FILE * f;
1445 int row, col;
1446 register struct ent ** pp;
1447 int pid;
1448 wchar_t out[FBUFLEN] = L"";
1449
1450 if (fname == NULL)
1451 f = stdout;
1452 else {
1453 sc_info("Writing file \"%s\"...", fname);
1454 if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
1455 sc_error ("Can't create file \"%s\"", fname);
1456 return;
1457 }
1458 }
1459
1460 // to prevent empty lines at the end of the file
1461 struct ent * ent = go_end();
1462 if (rn > ent->row) rn = ent->row;
1463 ent = goto_last_col(); // idem with columns
1464 if (cn > ent->col) cn = ent->col;
1465
1466 char num [FBUFLEN] = "";
1467 char text[FBUFLEN] = "";
1468 char formated_s[FBUFLEN] = "";
1469 char dashline[FBUFLEN] = "";
1470 int res = -1;
1471 int align = 1;
1472 int dash_num;
1473 int rowfmt;
1474
1475 for (row = r0; row <= rn; row++) {
1476 for (rowfmt=0; rowfmt<row_format[row]; rowfmt++) {
1477
1478 // ignore hidden rows
1479 //if (row_hidden[row]) continue;
1480
1481 for (pp = ATBL(tbl, row, col = c0); col <= cn; col++, pp++) {
1482 // ignore hidden cols
1483 //if (col_hidden[col]) continue;
1484
1485 if (col == c0) {
1486 (void) fprintf (f, "| ");
1487 } else if (col <= cn) {
1488 (void) fprintf (f, " | ");
1489 }
1490
1491 //make header border of dashes with alignment characters
1492 if (row == 0) {
1493 if (col == c0) strcat (dashline, "|");
1494 if (align == 0) {
1495 strcat (dashline, ":");
1496 } else {
1497 strcat (dashline, "-");
1498 }
1499 for (dash_num = 0; dash_num < fwidth[col]; dash_num++) {
1500 strcat (dashline, "-");
1501 }
1502 if(align >= 0) {
1503 strcat (dashline, ":");
1504 } else {
1505 strcat (dashline, "-");
1506 }
1507 strcat (dashline, "|");
1508 }
1509
1510 if (*pp) {
1511 num [0] = '\0';
1512 text[0] = '\0';
1513 out [0] = L'\0';
1514 formated_s[0] = '\0';
1515 res = -1;
1516 align = 1;
1517
1518 // If a numeric value exists
1519 if ( (*pp)->flags & is_valid) {
1520 res = ui_get_formated_value(pp, col, formated_s);
1521 // res = 0, indicates that in num we store a date
1522 // res = 1, indicates a format is applied in num
1523 if (res == 0 || res == 1) {
1524 strcpy(num, formated_s);
1525 } else if (res == -1) {
1526 sprintf(num, "%.*f", precision[col], (*pp)->v);
1527 }
1528 }
1529
1530 // If a string exists
1531 if ((*pp)->label) {
1532 strcpy(text, (*pp)->label);
1533 align = 1; // right alignment
1534 if ((*pp)->flags & is_label) { // center alignment
1535 align = 0;
1536 } else if ((*pp)->flags & is_leftflush) { // left alignment
1537 align = -1;
1538 } else if (res == 0) { // res must ¿NOT? be zero for label to be printed
1539 text[0] = '\0';
1540 }
1541 }
1542
1543
1544 pad_and_align (text, num, fwidth[col], align, 0, out, row_format[row]);
1545
1546 wchar_t new[wcslen(out)+1];
1547 wcscpy(new, out);
1548 int cw = count_width_widestring(new, fwidth[col]);
1549
1550 if (wcslen(new) > cw && rowfmt) {
1551 int count_row = 0;
1552 for (count_row = 0; count_row < rowfmt; count_row++) {
1553 cw = count_width_widestring(new, fwidth[col]);
1554 if (cw) del_range_wchars(new, 0, cw-1);
1555 int whites = fwidth[col] - wcslen(new);
1556 while (whites-- > 0) add_wchar(new, L' ', wcslen(new));
1557 }
1558 new[cw] = L'\0';
1559 fprintf (f, "%ls", new);
1560 } else if (! rowfmt && wcslen(new)) {
1561 if (get_conf_int("truncate") || !get_conf_int("overlap")) new[cw] = L'\0';
1562 fprintf (f, "%ls", new);
1563 } else {
1564 fprintf (f, "%*s", fwidth[col], " ");
1565 }
1566 } else {
1567 fprintf (f, "%*s", fwidth[col], " ");
1568 }
1569 }
1570 fprintf(f," |\n");
1571 }
1572
1573 if (row == 0) (void) fprintf(f,"%s\n",dashline);
1574 }
1575
1576 if (fname != NULL) {
1577 closefile(f, pid, 0);
1578 if (! pid) {
1579 sc_info("File \"%s\" written", fname);
1580 }
1581 }
1582 }
1583
1584 /**
1585 * \brief Export to plain TXT
1586 *
1587 * \param[in] fname file name
1588 * \param[in] r0
1589 * \param[in] c0
1590 * \param[in] rn
1591 * \param[in] cn
1592 *
1593 * \return none
1594 */
export_plain(char * fname,int r0,int c0,int rn,int cn)1595 void export_plain(char * fname, int r0, int c0, int rn, int cn) {
1596 FILE * f;
1597 int row, col;
1598 register struct ent ** pp;
1599 int pid;
1600 wchar_t out[FBUFLEN] = L"";
1601
1602 if (fname == NULL)
1603 f = stdout;
1604 else {
1605 sc_info("Writing file \"%s\"...", fname);
1606 if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
1607 sc_error ("Can't create file \"%s\"", fname);
1608 return;
1609 }
1610 }
1611
1612 // to prevent empty lines at the end of the file
1613 struct ent * ent = go_end();
1614 if (rn > ent->row) rn = ent->row;
1615 ent = goto_last_col(); // idem with columns
1616 if (cn > ent->col) cn = ent->col;
1617
1618 char num [FBUFLEN] = "";
1619 char text[FBUFLEN] = "";
1620 char formated_s[FBUFLEN] = "";
1621 int res = -1;
1622 int align = 1;
1623 int rowfmt;
1624
1625 for (row = r0; row <= rn; row++) {
1626 for (rowfmt=0; rowfmt<row_format[row]; rowfmt++) {
1627
1628 // ignore hidden rows
1629 //if (row_hidden[row]) continue;
1630
1631 for (pp = ATBL(tbl, row, col = c0); col <= cn; col++, pp++) {
1632 // ignore hidden cols
1633 //if (col_hidden[col]) continue;
1634
1635 if (*pp) {
1636
1637 num [0] = '\0';
1638 text[0] = '\0';
1639 out [0] = L'\0';
1640 formated_s[0] = '\0';
1641 res = -1;
1642 align = 1;
1643
1644 // If a numeric value exists
1645 if ( (*pp)->flags & is_valid) {
1646 res = ui_get_formated_value(pp, col, formated_s);
1647 // res = 0, indicates that in num we store a date
1648 // res = 1, indicates a format is applied in num
1649 if (res == 0 || res == 1) {
1650 strcpy(num, formated_s);
1651 } else if (res == -1) {
1652 sprintf(num, "%.*f", precision[col], (*pp)->v);
1653 }
1654 }
1655
1656 // If a string exists
1657 if ((*pp)->label) {
1658 strcpy(text, (*pp)->label);
1659 align = 1; // right alignment
1660 if ((*pp)->flags & is_label) { // center alignment
1661 align = 0;
1662 } else if ((*pp)->flags & is_leftflush) { // left alignment
1663 align = -1;
1664 } else if (res == 0) { // res must ¿NOT? be zero for label to be printed
1665 text[0] = '\0';
1666 }
1667 }
1668
1669 pad_and_align (text, num, fwidth[col], align, 0, out, row_format[row]);
1670
1671 wchar_t new[wcslen(out)+1];
1672 wcscpy(new, out);
1673 int cw = count_width_widestring(new, fwidth[col]);
1674
1675 if (wcslen(new) > cw && rowfmt) {
1676 int count_row = 0;
1677 for (count_row = 0; count_row < rowfmt; count_row++) {
1678 cw = count_width_widestring(new, fwidth[col]);
1679 if (cw) del_range_wchars(new, 0, cw-1);
1680 int whites = fwidth[col] - wcslen(new);
1681 while (whites-- > 0) add_wchar(new, L' ', wcslen(new));
1682 }
1683 new[cw] = L'\0';
1684 fprintf (f, "%ls", new);
1685 } else if (! rowfmt && wcslen(new)) {
1686 if (get_conf_int("truncate") || !get_conf_int("overlap")) new[cw] = L'\0';
1687 fprintf (f, "%ls", new);
1688 } else {
1689 fprintf (f, "%*s", fwidth[col], " ");
1690 }
1691 } else {
1692 fprintf (f, "%*s", fwidth[col], " ");
1693 }
1694 }
1695 fprintf(f,"\n");
1696 }
1697 }
1698 if (fname != NULL) {
1699 closefile(f, pid, 0);
1700 if (! pid) {
1701 sc_info("File \"%s\" written", fname);
1702 }
1703 }
1704
1705 }
1706
export_latex(char * fname,int r0,int c0,int rn,int cn,int verbose)1707 void export_latex(char * fname, int r0, int c0, int rn, int cn, int verbose) {
1708 FILE * f;
1709 int row, col;
1710 register struct ent ** pp;
1711 int pid;
1712
1713 // to prevent empty lines at the end of the file
1714 struct ent * ent = go_end();
1715 if (rn > ent->row) rn = ent->row;
1716 ent = goto_last_col(); // idem with columns
1717 if (cn > ent->col) cn = ent->col;
1718
1719 if (verbose) sc_info("Writing file \"%s\"...", fname);
1720
1721 if (fname == NULL)
1722 f = stdout;
1723 else {
1724 if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
1725 if (verbose) sc_error ("Can't create file \"%s\"", fname);
1726 return;
1727 }
1728 }
1729
1730 // do the stuff
1731 fprintf(f,"%% ** SC-IM spreadsheet output\n\\begin{tabular}{");
1732 for (col=c0;col<=cn; col++) fprintf(f,"c");
1733 fprintf(f, "}\n");
1734 char coldelim = '&';
1735 for (row=r0; row<=rn; row++) {
1736 for (pp = ATBL(tbl, row, col=c0); col<=cn; col++, pp++) {
1737 if (*pp) {
1738 char *s;
1739 if ((*pp)->flags & is_valid) {
1740 if ((*pp)->cellerror) {
1741 (void) fprintf (f, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
1742 } else if ((*pp)->format) {
1743 char field[FBUFLEN];
1744 if (*((*pp)->format) == ctl('d')) {
1745 time_t v = (time_t) ((*pp)->v);
1746 strftime(field, sizeof(field), ((*pp)->format)+1, localtime(&v));
1747 } else
1748 format((*pp)->format, precision[col], (*pp)->v, field, sizeof(field));
1749 unspecial(f, field, coldelim);
1750 } else {
1751 char field[FBUFLEN];
1752 (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp) -> v, field, sizeof(field));
1753 unspecial(f, field, coldelim);
1754 }
1755 }
1756 if ((s = (*pp)->label)) unspecial(f, s, coldelim);
1757 }
1758 if (col < cn) fprintf(f,"%c", coldelim);
1759 }
1760 if (row < rn) (void) fprintf (f, "\\\\");
1761 fprintf(f,"\n");
1762 }
1763
1764 fprintf(f,"\\end{tabular}\n%% ** end of SC-IM spreadsheet output\n");
1765
1766 if (fname != NULL) closefile(f, pid, 0);
1767
1768 if (! pid && verbose) sc_info("File \"%s\" written", fname);
1769 }
1770
1771 /**
1772 * \brief TODO Document unspecial()
1773 *
1774 * \details Unspecial (backquotes - > ") things that are special
1775 * chars in a table
1776 *
1777 * \param[in] f file pointer
1778 * \param[in] srt string pointer
1779 * \param[in] delim
1780 *
1781 * \return none
1782 */
unspecial(FILE * f,char * str,int delim)1783 void unspecial(FILE * f, char * str, int delim) {
1784 int backquote = 0;
1785
1786 if (str_in_str(str, ",") != -1) backquote = 1;
1787 if (backquote) putc('\"', f);
1788 if (*str == '\\') str++; // delete wheeling string operator, OK?
1789 while (*str) {
1790 // for LATEX export
1791 if (delim == '&' && ( (*str == '&') || (*str == '$') ||
1792 (*str == '#') || (*str == '%') || (*str == '{') || (*str == '}') || (*str == '&')))
1793 putc('\\', f);
1794 putc(*str, f);
1795 str++;
1796 }
1797 if (backquote) putc('\"', f);
1798 }
1799
1800 /**
1801 * \brief TODO Document export_delim
1802 *
1803 * \param[in] fname full path of the file
1804 * \param[in] coldelim
1805 * \param[in] r0
1806 * \param[in] c0
1807 * \param[in] rn
1808 * \param[in] cn
1809 * \param[in] verbose
1810 *
1811 * \return none
1812 */
export_delim(char * fname,char coldelim,int r0,int c0,int rn,int cn,int verbose)1813 void export_delim(char * fname, char coldelim, int r0, int c0, int rn, int cn, int verbose) {
1814 FILE * f;
1815 int row, col;
1816 register struct ent ** pp;
1817 int pid;
1818
1819 // to prevent empty lines at the end of the file
1820 struct ent * ent = go_end();
1821 if (rn > ent->row) rn = ent->row;
1822 ent = goto_last_col(); // idem with columns
1823 if (cn > ent->col) cn = ent->col;
1824
1825 if (verbose) sc_info("Writing file \"%s\"...", fname);
1826
1827 if (fname == NULL)
1828 f = stdout;
1829 else {
1830 if ((f = openfile(fname, &pid, NULL)) == (FILE *)0) {
1831 if (verbose) sc_error ("Can't create file \"%s\"", fname);
1832 return;
1833 }
1834 }
1835
1836 for (row = r0; row <= rn; row++) {
1837 for (pp = ATBL(tbl, row, col = c0); col <= cn; col++, pp++) {
1838 int last_valid_col = right_limit(row)->col; // for issue #374
1839 if (col > last_valid_col) continue;
1840 if (*pp) {
1841 char * s;
1842 if ((*pp)->flags & is_valid) {
1843 if ((*pp)->cellerror) {
1844 (void) fprintf (f, "%*s", fwidth[col], ((*pp)->cellerror == CELLERROR ? "ERROR" : "INVALID"));
1845 } else if ((*pp)->format) {
1846 char field[FBUFLEN];
1847 if (*((*pp)->format) == 'd') { // Date format
1848 time_t v = (time_t) ((*pp)->v);
1849 strftime(field, sizeof(field), ((*pp)->format)+1, localtime(&v));
1850 } else { // Numeric format
1851 format((*pp)->format, precision[col], (*pp)->v, field, sizeof(field));
1852 }
1853 ltrim(field, ' ');
1854 unspecial(f, field, coldelim);
1855 } else { //eng number format
1856 char field[FBUFLEN] = "";
1857 (void) engformat(realfmt[col], fwidth[col], precision[col], (*pp)->v, field, sizeof(field));
1858 ltrim(field, ' ');
1859 unspecial(f, field, coldelim);
1860 }
1861 }
1862 if ((s = (*pp)->label)) {
1863 ltrim(s, ' ');
1864 unspecial(f, s, coldelim);
1865 }
1866 }
1867 if (col < cn && col < last_valid_col)
1868 (void) fprintf(f,"%c", coldelim);
1869 }
1870 (void) fprintf(f,"\n");
1871 }
1872 if (fname != NULL)
1873 closefile(f, pid, 0);
1874
1875 if (! pid && verbose) {
1876 sc_info("File \"%s\" written", fname);
1877 }
1878 }
1879
1880 /**
1881 * \brief Check what is the max length of all the lines in a file
1882 *
1883 * \details Check the maximum length of lines in a file. Note:
1884 * FILE * f shall be opened.
1885 *
1886 * \param[in] f file pointer
1887 * \return file length + 1
1888 */
max_length(FILE * f)1889 int max_length(FILE * f) {
1890 if (f == NULL) return -1;
1891 int count = 0, max = 0;
1892 int c = fgetc(f);
1893
1894 while (c != EOF) {
1895 if (c != '\n') {
1896 count++;
1897 } else if (count > max) {
1898 max = count;
1899 count = 0;
1900 } else {
1901 count = 0;
1902 }
1903 c = fgetc(f);
1904 }
1905 return max + 1;
1906 }
1907
1908 /**
1909 * \brief Check the number of lines of a file
1910 *
1911 * \details Check the numbers of lines of a file. it count \n chars.
1912 * FILE * f shall be opened.
1913 *
1914 * \param[in] f file pointer
1915 * \return number
1916 */
count_lines(FILE * f)1917 int count_lines(FILE * f) {
1918 int count = 0;
1919 if (f == NULL) return count;
1920 int c = fgetc(f);
1921
1922 while (c != EOF) {
1923 if (c == '\n') count++;
1924 c = fgetc(f);
1925 }
1926 return count;
1927 }
1928
1929 /**
1930 * \brief TODO Document plugin_exists()
1931 *
1932 * \param[in] name
1933 * \param[in] len
1934 * \param[in] path
1935 *
1936 * \return none
1937 */
plugin_exists(char * name,int len,char * path)1938 int plugin_exists(char * name, int len, char * path) {
1939 FILE * fp;
1940 static char * HomeDir;
1941 char cwd[1024];
1942
1943 if (getcwd(cwd, sizeof(cwd)) != NULL) {
1944 strcpy((char *) path, cwd);
1945 strcat((char *) path, "/");
1946 strncat((char *) path, name, len);
1947 if ((fp = fopen((char *) path, "r"))) {
1948 fclose(fp);
1949 return 1;
1950 }
1951 }
1952 /* Check XDG_CONFIG_HOME */
1953 if ((HomeDir = getenv("XDG_CONFIG_HOME"))) {
1954 sprintf((char *) path, "%s/sc-im/%s", HomeDir, name);
1955 if ((fp = fopen((char *) path, "r"))) {
1956 fclose(fp);
1957 return 1;
1958 }
1959 }
1960 /* Check compile time path (default ~/.config/sc-im) */
1961 if ((HomeDir = getenv("HOME"))) {
1962 sprintf((char *) path, "%s/%s/%s", HomeDir, CONFIG_DIR, name);
1963 if ((fp = fopen((char *) path, "r"))) {
1964 fclose(fp);
1965 return 1;
1966 }
1967 }
1968 /* LEGACY PATH */
1969 if ((HomeDir = getenv("HOME"))) {
1970 sprintf((char *) path, "%s/.scim/%s", HomeDir, name);
1971 if ((fp = fopen((char *) path, "r"))) {
1972 fclose(fp);
1973 return 1;
1974 }
1975 }
1976 strcpy((char *) path, HELP_PATH);
1977 strcat((char *) path, "/");
1978 strncat((char *) path, name, len);
1979 if ((fp = fopen((char *) path, "r"))) {
1980 fclose(fp);
1981 return 1;
1982 }
1983 return 0;
1984 }
1985
1986 /**
1987 * \brief TODO Document do_autobackup()
1988 * \return none
1989 */
do_autobackup()1990 void * do_autobackup() {
1991 int len = strlen(curfile);
1992 //if (loading || ! len) return (void *) -1;
1993 //if (! len || ! modflg) return (void *) -1;
1994 if (! len) return (void *) -1;
1995
1996 char * pstr = strrchr(curfile, '/');
1997 int pos = pstr == NULL ? -1 : pstr - curfile;
1998 char name[PATHLEN] = {'\0'};
1999 char namenew[PATHLEN] = {'\0'};
2000 strcpy(name, curfile);
2001 add_char(name, '.', pos+1);
2002 sprintf(name + strlen(name), ".bak");
2003 sprintf(namenew, "%.*s.new", PATHLEN-5, name);
2004 //if (get_conf_int("debug")) sc_info("doing autobackup of file:%s", name);
2005
2006 // create new version
2007 if (! strcmp(&name[strlen(name)-7], ".sc.bak")) {
2008 register FILE * f;
2009 if ((f = fopen(namenew , "w")) == NULL) return (void *) -1;
2010 write_fd(f, 0, 0, maxrow, maxcol);
2011 fclose(f);
2012 } else if (! strcmp(&name[strlen(name)-8], ".csv.bak")) {
2013 export_delim(namenew, get_delim("csv"), 1, 0, maxrow, maxcol, 0);
2014 #ifdef XLSX_EXPORT
2015 } else if (! strcmp(&name[strlen(name)-9], ".xlsx.bak")) {
2016 export_delim(namenew, ',', 0, 0, maxrow, maxcol, 0);
2017 export_xlsx(namenew, 0, 0, maxrow, maxcol);
2018 #endif
2019 } else if (! strcmp(&name[strlen(name)-8], ".tab.bak") || ! strcmp(&name[strlen(name)-8], ".tsv.bak")) {
2020 export_delim(namenew, '\t', 0, 0, maxrow, maxcol, 0);
2021 }
2022
2023 // delete if exists name
2024 remove(name);
2025
2026 // rename name.new to name
2027 rename(namenew, name);
2028
2029 return (void *) 0;
2030 }
2031
2032 /**
2033 * \brief Check if it is time to do an autobackup
2034 * \return none
2035 */
handle_backup()2036 void handle_backup() {
2037 #ifdef AUTOBACKUP
2038 extern struct timeval lastbackup_tv; // last backup timer
2039 extern struct timeval current_tv; //runtime timer
2040
2041 int autobackup = get_conf_int("autobackup");
2042 if (autobackup && autobackup > 0 && (current_tv.tv_sec - lastbackup_tv.tv_sec > autobackup || (lastbackup_tv.tv_sec == 0 && lastbackup_tv.tv_usec == 0))) {
2043 #ifdef HAVE_PTHREAD
2044 if (pthread_exists) pthread_join (fthread, NULL);
2045 pthread_exists = (pthread_create(&fthread, NULL, do_autobackup, NULL) == 0) ? 1 : 0;
2046 #else
2047 do_autobackup();
2048 #endif
2049 gettimeofday(&lastbackup_tv, NULL);
2050 }
2051 #endif
2052 return;
2053 }
2054
2055 /**
2056 * \brief Remove autobackup file
2057 * \details Remove autobackup file. Used when quitting or when loading
2058 * a new file.
2059 * \param[in] file file pointer
2060 * \return none
2061 */
remove_backup(char * file)2062 void remove_backup(char * file) {
2063 int len = strlen(file);
2064 if (!len) return;
2065 char * pstr = strrchr(file, '/');
2066 int pos = pstr == NULL ? -1 : pstr - file;
2067 char name[len+6];
2068 strcpy(name, file);
2069 add_char(name, '.', pos+1);
2070 sprintf(name + strlen(name), ".bak");
2071 remove(name);
2072 return;
2073 }
2074
2075 /**
2076 * \brief TODO Document backup_exists()
2077 * \param[in] file file pointer
2078 * \return none
2079 */
backup_exists(char * file)2080 int backup_exists(char * file) {
2081 int len = strlen(file);
2082 if (!len) return 0;
2083 char * pstr = strrchr(file, '/');
2084 int pos = pstr == NULL ? -1 : pstr - file;
2085 char name[len+6];
2086 strcpy(name, file);
2087 add_char(name, '.', pos+1);
2088 sprintf(name + strlen(name), ".bak");
2089 FILE * fp;
2090 if ((fp = fopen((char *) name, "r"))) {
2091 fclose(fp);
2092 return 1;
2093 }
2094 return 0;
2095 }
2096
2097 /**
2098 * \brief open file nested
2099 * \param[in] file name string
2100 * \return none
2101 */
openfile_nested(char * file)2102 void openfile_nested(char * file) {
2103 char * cmd = get_conf_value("default_open_file_under_cursor_cmd");
2104 if (cmd == NULL || ! strlen(cmd)) return;
2105 char syscmd[PATHLEN + strlen(cmd)];
2106 sprintf(syscmd, "%s", cmd);
2107 sprintf(syscmd + strlen(syscmd), " %s", file);
2108 system(syscmd);
2109 }
2110
2111 /**
2112 * \brief open file under cursor
2113 * \param[in] current row and column
2114 * \return none
2115 */
openfile_under_cursor(int r,int c)2116 void openfile_under_cursor(int r, int c) {
2117 register struct ent ** pp;
2118 pp = ATBL(tbl, r, c);
2119 if (*pp && (*pp)->label) {
2120 char text[FBUFLEN] = "";
2121 strcpy(text, (*pp)->label);
2122 openfile_nested(text);
2123 }
2124 }
2125