1 /*
2  * $Id: sylk.c,v 1.18 2000/08/10 21:02:51 danny Exp $
3  *
4  * Copyright � 1990, 1992, 1993 Free Software Foundation, Inc.
5  *
6  * This file is part of Oleo, the GNU Spreadsheet.
7  *
8  * Oleo is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * Oleo is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Oleo; see the file COPYING.  If not, write to
20  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #ifdef	WITH_DMALLOC
28 #include <dmalloc.h>
29 #endif
30 
31 #include "funcdef.h"
32 #include <stdio.h>
33 #include <ctype.h>
34 #include "sysdef.h"
35 #include "io-generic.h"
36 #include "io-abstract.h"
37 #include "global.h"
38 #include "cell.h"
39 #include "line.h"
40 #include "io-term.h"
41 #include "lists.h"
42 #include "io-utils.h"
43 #include "ref.h"
44 #include "regions.h"
45 #include "window.h"
46 #include "info.h"
47 #include "cmd.h"
48 
49 
50 /* Forward declaration */
51 static char * sylk_to_oleo_functions(char *f);
52 
53 #define	SYLK_LEN	1024
54 
55 /* FIX ME Where does this variable belong ?? */
56 static int	nformat = 0;		/* We've already read this many formats */
57 /*
58  * These functions read and write Microsoft Multiplan SYLK style files
59  * as well as SYLK-NOA0 files.  SYLK-NOA0 is the same as SYLK except that
60  * cell references are in rc format instead of a0 format.
61  */
62 
63 void
sylk_read_file(fp,ismerge)64 sylk_read_file (fp, ismerge)
65      FILE *fp;
66      int ismerge;
67 {
68   char *ptr;
69   CELLREF crow = 0, ccol = 0, czrow = 0, czcol = 0;
70   int lineno;
71   char cbuf[SYLK_LEN];
72   char expbuf[SYLK_LEN];
73   char *vname, *vval;
74   int vlen = 0;
75   int cprot;
76   char *cexp, *cval;
77   CELL *cp;
78   struct rng rng;
79   int fmt = 0, prc = 0;
80   int jst = 0;
81   long mx_row = MAX_ROW, mx_col = MAX_COL;
82   int next_a0;
83   int old_a0;
84   int	num;
85 
86   old_a0 = Global->a0;
87   next_a0 = old_a0;
88   Global->a0 = Global->sylk_a0;
89 
90   lineno = 0;
91   if (!ismerge)
92     clear_spreadsheet ();
93   while (fgets (cbuf, sizeof (cbuf), fp))
94     {
95       lineno++;
96 #if 0
97       if (lineno % 50 == 0)
98 	io_info_msg ("Line %d", lineno);
99 #endif
100 
101       /* Remove newline */
102       if ((ptr = (char *)index (cbuf, '\n')))
103 	*ptr = '\0';
104 
105       /* Also remove DOS style newline */
106       if ((ptr = (char *)index (cbuf, '\r')))
107 	*ptr = '\0';
108 
109       ptr = cbuf;
110 
111 /*
112  * The first character on each line
113  */
114       switch (*ptr) {
115 	/* First character on the line */
116 	case 'I':		/* ID field, ignored */
117 	  if (ptr[1] != 'D' || ptr[2] != ';')
118 	    goto bad_field;
119 	  if (strcmp(ptr+4, "OLEO") != 0) {
120 		if (strncmp(ptr+4, "WXL", 3) == 0)
121 			io_info_msg("Reading SYLK file from Windows Excel\n");
122 		else
123 			io_info_msg("Reading SYLK file from '%s'\n", ptr+4);
124 	  }
125 	  break;
126 
127 	/* First character on the line */
128 	case 'F':		/* Format field */
129 	  vlen = 0;
130 	  ptr++;
131 	  while (*ptr) {
132 	      if (*ptr != ';')
133 		goto bad_field;
134 	      ptr++;
135 
136 	      /* Format field, second code
137 	       * F;P0;DG0G8;M264
138 	       *   ^
139 	       */
140 	      switch (*ptr++) {
141 		  int clo, chi, cwid;
142 		/* Format next field code */
143 		case 'C':	/* Column from rows 1 to 255 */
144 		  czcol = astol (&ptr);
145 		  vlen = 2;
146 		  break;
147 
148 		/* Format next field code */
149 		case 'D':	/* Default format */
150 		  switch (*ptr++) {
151 		    case 'G':
152 		      default_fmt = FMT_GEN;
153 		      break;
154 		    case 'E':
155 		      default_fmt = FMT_EXP;
156 		      break;
157 		    case 'F':
158 		      default_fmt = FMT_FXT;
159 		      break;
160 		    case '$':
161 		      default_fmt = FMT_DOL;
162 		      break;
163 		    case '*':	/* * format implemented as +- format */
164 		      default_fmt = FMT_GPH;
165 		      break;
166 		    case ',':	/* JF */
167 		      default_fmt = FMT_CMA;
168 		      break;
169 		    case 'U':
170 		      default_fmt = FMT_USR;
171 		      break;
172 		    case '%':
173 		      default_fmt = FMT_PCT;
174 		      break;
175 		    case 'H':
176 		      default_fmt = FMT_HID;
177 		      break;
178 		      /* End of JF */
179 		    case 'C':	/* Continuous not supported */
180 		    default:
181 		      io_error_msg ("Line %d: format %c not supported", lineno, ptr[-1]);
182 		      break;
183 		    }
184 
185 		  if (*ptr == 'F')
186 		    {
187 		      default_prc = FLOAT_PRECISION;
188 		      ptr++;
189 		    }
190 		  else
191 		    default_prc = astol (&ptr);
192 
193 		  /* Justification */
194 		  switch (*ptr++) {
195 		    case 'C':
196 		      default_jst = JST_CNT;
197 		      break;
198 		    case 'L':
199 		      default_jst = JST_LFT;
200 		      break;
201 		    case 'R':
202 		      default_jst = JST_RGT;
203 		      break;
204 		    case 'G':	/* General format not supported */
205 			/* FIX ME this is also in Excel */
206 		      break;
207 		    default:
208 		      io_error_msg ("Line %d: Alignment %c not supported\n", lineno, ptr[-1]);
209 		      break;
210 		    }
211 		  default_width = astol (&ptr);
212 		  break;
213 
214 		/* Format next field code */
215 		case 'F':
216 		  switch (*ptr++)
217 		    {
218 		    case 'D':
219 		      fmt = FMT_DEF;
220 		      break;
221 		    case 'G':
222 		      fmt = FMT_GEN;
223 		      break;
224 		    case 'E':
225 		      fmt = FMT_EXP;
226 		      break;
227 		    case 'F':
228 		      fmt = FMT_FXT;
229 		      break;
230 		    case '$':
231 		      fmt = FMT_DOL;
232 		      break;
233 		    case '*':	/* JF implemented as +- format */
234 		      fmt = FMT_GPH;
235 		      break;
236 		    case ',':	/* JF */
237 		      fmt = FMT_CMA;
238 		      break;
239 		    case 'U':
240 		      fmt = FMT_USR;
241 		      break;
242 		    case '%':
243 		      fmt = FMT_PCT;
244 		      break;
245 		    case 'H':
246 		      fmt = FMT_HID;
247 		      break;	/* END of JF */
248 		    case 'C':
249 		    default:
250 		      io_error_msg ("Line %d: format %c not supported", lineno, ptr[-1]);
251 		      fmt = FMT_DEF;
252 		      break;
253 		    }
254 
255 		  if (*ptr == 'F')
256 		    {
257 		      prc = FLOAT_PRECISION;
258 		      ptr++;
259 		    }
260 		  else
261 		    prc = astol (&ptr);
262 
263 		  switch (*ptr++)
264 		    {
265 		    case 'C':
266 		      jst = JST_CNT;
267 		      break;
268 		    case 'L':
269 		      jst = JST_LFT;
270 		      break;
271 		    case 'R':
272 		      jst = JST_RGT;
273 		      break;
274 		    case 'D':
275 		      jst = JST_DEF;
276 		      break;
277 		    case 'G':
278 		      break;
279 		    default:
280 		      io_error_msg ("Line %d: Alignment %c not supported", lineno, ptr[-1]);
281 		      jst = JST_DEF;
282 		      break;
283 		    }
284 		  vlen = 1;
285 		  break;
286 
287 		/* Format next field code */
288 		case 'R':	/* Row from cols 1 to 63 */
289 		  czrow = astol (&ptr);
290 		  vlen = 4;
291 		  break;
292 
293 		/* Format next field code */
294 		case 'W':	/* Width of clo to chi is cwid */
295 		  clo = astol (&ptr);
296 		  chi = astol (&ptr);
297 		  cwid = astol (&ptr) + 1;
298 		  for (; clo <= chi; clo++)
299 		    set_width (clo, cwid);
300 		  break;
301 
302 		/* Format next field code */
303 		case 'H':	/* JF: extension */
304 		  clo = astol (&ptr);
305 		  chi = astol (&ptr);
306 		  cwid = astol (&ptr) + 1;
307 		  for (; clo <= chi; clo++)
308 		    set_height (clo, cwid);
309 		  break;
310 
311 		/* Format next field code */
312 		case 'X':
313 		  ccol = astol (&ptr);
314 		  break;
315 
316 		/* Format next field code */
317 		case 'Y':
318 		  crow = astol (&ptr);
319 		  break;
320 
321 		default:
322 		  goto bad_field;
323 
324 		case 'P':	/* FIX ME Excel */
325 			num = 0;
326 			while (*ptr && *ptr != ';') {
327 				if (isdigit(*ptr))
328 					num = num * 10 + *ptr - '0';	/* ASCII */
329 				ptr++;
330 			}
331 			break;
332 
333 		case 'M':	/* FIX ME Excel */
334 			while (*ptr && *ptr != ';') ptr++;
335 			break;
336 
337 		case 'S':	/* FIX ME Excel */
338 			while (*ptr && *ptr != ';') ptr++;
339 			break;
340 		}
341 	    }
342 
343 	  switch (vlen)
344 	    {
345 	    case 1:
346 	      cp = find_or_make_cell (crow, ccol);
347 	      SET_FORMAT (cp, fmt);
348 	      SET_PRECISION(cp, prc);
349 	      SET_JST (cp, jst);
350 	      break;
351 	    case 2:
352 	      rng.lr = MIN_ROW;
353 	      rng.lc = czcol;
354 	      rng.hr = mx_row;
355 	      rng.hc = czcol;
356 	      make_cells_in_range (&rng);
357 	      while ((cp = next_cell_in_range ()))
358 		{
359 		  SET_FORMAT (cp, fmt);
360 		  SET_PRECISION(cp, prc);
361 		  SET_JST (cp, jst);
362 		}
363 	      break;
364 	    case 4:
365 	      rng.lr = czrow;
366 	      rng.lc = MIN_COL;
367 	      rng.hr = czrow;
368 	      rng.hc = mx_col;
369 	      make_cells_in_range (&rng);
370 	      while ((cp = next_cell_in_range ()))
371 		{
372 		  SET_FORMAT (cp, fmt);
373 		  SET_PRECISION(cp, prc);
374 		  SET_JST (cp, jst);
375 		}
376 	      break;
377 	    default:
378 	      break;
379 	    }
380 
381 	  break;
382 
383 	/* First character on the line */
384 	case 'B':		/* Boundry field, ignored */
385 	  ptr++;
386 	  while (*ptr) {
387 	      if (*ptr != ';')
388 		goto bad_field;
389 	      ptr++;
390 	      switch (*ptr++) {
391 		case 'X':
392 		  mx_col = astol (&ptr);
393 		  if (mx_col > MAX_COL)
394 		    {
395 		      io_error_msg ("Boundry column %lu too large!", mx_col);
396 		      mx_col = MAX_COL;
397 		    }
398 		  break;
399 		case 'Y':
400 		  mx_row = astol (&ptr);
401 		  if (mx_row > MAX_ROW)
402 		    {
403 		      io_error_msg ("Boundry row %lu too large!", mx_row);
404 		      mx_row = MAX_ROW;
405 		    }
406 		  break;
407 		case 'D':	/* FIX ME Excel */
408 		  while (*ptr && *ptr != ';') ptr++;
409 		  break;
410 		default:
411 		  goto bad_field;
412 		}
413 	    }
414 	  break;
415 
416 	/* First character on the line */
417 	case 'N':		/* A Name field */
418 	  if (ptr[1] != 'N')
419 	    goto bad_field;
420 	  ptr += 2;
421 	  vname = 0;
422 	  vval = 0;
423 	  while (*ptr)
424 	    {
425 	      if (*ptr != ';')
426 		goto bad_field;
427 	      *ptr++ = '\0';
428 	      switch (*ptr++)
429 		{
430 		case 'N':	/* Name is */
431 		  vname = ptr;
432 		  while (*ptr && *ptr != ';')
433 		    ptr++;
434 		  vlen = ptr - vname;
435 		  break;
436 		case 'E':	/* Expression is */
437 		  vval = ptr;
438 		  while (*ptr && *ptr != ';')
439 		    ptr++;
440 		  break;
441 		default:
442 		  --ptr;
443 		  goto bad_field;
444 		}
445 	    }
446 	  if (!vname || !vval)
447 	    goto bad_field;
448 	  *ptr = '\0';
449 	  ptr = old_new_var_value (vname, vlen, vval);
450 	  if (ptr)
451 	    io_error_msg ("Line %d: Couldn't set %.*s to %s: %s", lineno, vlen, vname, vval, ptr);
452 	  break;
453 
454 	/* First character on the line */
455 	case 'C':		/* A Cell entry */
456 	  cprot = 0;
457 	  cval = 0;
458 	  cexp = 0;
459 	  cval = 0;
460 	  ptr++;
461 	  while (*ptr)
462 	    {
463 	      int quotes;
464 
465 	      if (*ptr != ';')
466 		goto bad_field;
467 	      *ptr++ = '\0';
468 	      switch (*ptr++)
469 		{
470 		case 'X':
471 		  ccol = astol (&ptr);
472 		  break;
473 		case 'Y':
474 		  crow = astol (&ptr);
475 		  break;
476 		case 'R':
477 		  czrow = astol (&ptr);
478 		  break;
479 		case 'C':
480 		  czcol = astol (&ptr);
481 		  break;
482 		case 'P':	/* This cell is Protected */
483 		  cprot++;
484 		  break;
485 		case 'K':	/* This cell's Konstant value */
486 		  cval = ptr;
487 		  quotes = 0;
488 		  while (*ptr && (*ptr != ';' || quotes > 0))
489 		    if (*ptr++ == '"')
490 		      quotes = !quotes;
491 		  break;
492 		case 'E':	/* This cell's Expression */
493 		  cexp = ptr;
494 		  quotes = 0;
495 		  while (*ptr && (*ptr != ';' || quotes > 0))
496 		    if (*ptr++ == '"')
497 		      quotes = !quotes;
498 
499 		  break;
500 		case 'G':
501 		  strcpy (expbuf, cval);
502 		  break;
503 		case 'D':
504 		  strcpy (expbuf, cexp);
505 		  break;
506 		case 'S':
507 		  cexp = expbuf;
508 		  break;
509 		default:
510 		  --ptr;
511 		  goto bad_field;
512 		}
513 	    }
514 	  *ptr = '\0';
515 	  if (cexp && cval && strcmp (cexp, cval))
516 	    {
517 	      cexp = sylk_to_oleo_functions(cexp);
518 	      ptr = read_new_value (crow, ccol, cexp, cval);
519 	      if (ptr)
520 		{
521 		  io_error_msg ("Line %d: %d,%d: Read '%s' %s", lineno, crow, ccol, cexp, ptr);
522 		  break;
523 		}
524 	    }
525 	  else if (cval)
526 	    {
527 	      ptr = read_new_value (crow, ccol, 0, cval);
528 	      if (ptr)
529 		{
530 		  io_error_msg ("Line %d: %d,%d: Val '%s' %s", lineno, crow, ccol, cexp, ptr);
531 		  break;
532 		}
533 	    }
534 	  else if (cexp)
535 	    {
536 	      cexp = sylk_to_oleo_functions(cexp);
537 	      ptr = read_new_value (crow, ccol, cexp, 0);
538 	      if (ptr)
539 		{
540 		  io_error_msg ("Line %d: %d,%d: Exp '%s' %s", lineno, crow, ccol, cexp, ptr);
541 		  break;
542 		}
543 	    }
544 	  if (cprot)
545 	    SET_LCK (find_or_make_cell (crow, ccol), LCK_LCK);
546 	  if (ismerge)
547 	    push_cell (crow, ccol);
548 	  /* ... */
549 	  break;
550 
551 	/* First character on the line */
552 	case 'E':
553 	  break;
554 
555 	/* First character on the line */
556 	case 'W':
557 	  io_read_window_config (ptr + 2);
558 	  break;
559 
560 	/* First character on the line */
561 	case 'U':
562 	  /* JF extension:  read user-defined formats */
563 	  read_mp_usr_fmt (ptr + 1);
564 	  break;
565 
566 	/* First character on the line */
567 	case 'O':
568 	  /* JF extension: read uset-settable options */
569 	  Global->a0 = next_a0;
570 
571 #if 0
572 	/* FIX ME
573 	   This is madness; options from SYLK should be translated into Oleo
574 	   internals before passing them to general option processing.
575 	 */
576 	  read_mp_options (ptr + 2);
577 #endif
578 	  next_a0 = Global->a0;
579 	  Global->a0 = Global->sylk_a0;
580 	  break;
581 
582 	/* First character on the line */
583 	default:
584 	bad_field:
585 	  Global->a0 = old_a0;
586 	  if (!ismerge)
587 	    clear_spreadsheet ();
588 	  io_recenter_all_win ();
589 	  io_error_msg ("Line %d: Unknown SYLK line \"%s\" (field %c)", lineno, cbuf, *ptr);
590 	  return;
591 
592 	/* First character on the line */
593 	case 'P':	/* FIX ME Excel */
594 		/* Probably define a format */
595 		nformat++;
596 		fprintf(stderr, "Format %d is %s\n", nformat, ptr);
597 		break;
598 	}
599     }
600   Global->a0 = next_a0;
601   io_recenter_all_win ();
602 }
603 
604 
605 
606 static char *
sylk_fmt_to_str(int f1,int p1)607 sylk_fmt_to_str (int f1, int p1)
608 {
609   static char p_buf[40];
610 
611   p_buf[1] = '\0';
612   switch (f1)
613     {
614     case FMT_DEF:
615       p_buf[0] = 'D';
616       break;
617     case FMT_HID:
618       p_buf[0] = 'H';
619       break;
620     case FMT_GPH:
621       p_buf[0] = '*';
622       break;
623     default:
624       if (p1 == FLOAT_PRECISION)
625 	{
626 	  p_buf[1] = 'F';
627 	  p_buf[2] = '\0';
628 	}
629       else
630 	sprintf (&p_buf[1], "%d", p1);
631 
632       switch (f1)
633 	{
634 	case FMT_USR:
635 	  p_buf[0] = 'U';
636 	  break;
637 	case FMT_GEN:
638 	  p_buf[0] = 'G';
639 	  break;
640 	case FMT_DOL:
641 	  p_buf[0] = '$';
642 	  break;
643 	case FMT_PCT:
644 	  p_buf[0] = '%';
645 	  break;
646 	case FMT_FXT:
647 	  p_buf[0] = 'F';
648 	  break;
649 	case FMT_CMA:
650 	  p_buf[0] = ',';
651 	  break;
652 	case FMT_EXP:
653 	  p_buf[0] = 'E';
654 	  break;
655 	default:
656 	  p_buf[0] = '?';
657 	  break;
658 	}
659       break;
660     }
661   return p_buf;
662 }
663 
664 static char
jst_to_chr(just)665 jst_to_chr (just)
666      int just;
667 {
668   switch (just)
669     {
670     case JST_DEF:
671       return 'D';
672     case JST_LFT:
673       return 'L';
674     case JST_RGT:
675       return 'R';
676     case JST_CNT:
677       return 'C';
678     default:
679       return '?';
680     }
681 }
682 
683 static FILE *sylk_fp;
684 static struct rng *sylk_rng;
685 
686 static void
sylk_write_var(name,var)687 sylk_write_var (name, var)
688      char *name;
689      struct var *var;
690 {
691   if (var->var_flags == VAR_UNDEF && (!var->var_ref_fm || var->var_ref_fm->refs_used == 0))
692     return;
693   switch (var->var_flags)
694     {
695     case VAR_UNDEF:
696       break;
697     case VAR_CELL:
698       if (var->v_rng.lr >= sylk_rng->lr && var->v_rng.lr <= sylk_rng->hr && var->v_rng.lc >= sylk_rng->lc && var->v_rng.lc <= sylk_rng->hc)
699 	(void) fprintf (sylk_fp, "NN;N%s;E%s\n", var->var_name, cell_name (var->v_rng.lr, var->v_rng.lc));
700       break;
701     case VAR_RANGE:
702       if (var->v_rng.lr < sylk_rng->lr || var->v_rng.hr > sylk_rng->hr || var->v_rng.lc < sylk_rng->lc || var->v_rng.hc > sylk_rng->hc)
703 	break;
704 
705       (void) fprintf (sylk_fp, "NN;N%s;E%s\n", var->var_name, range_name (&(var->v_rng)));
706       break;
707 #ifdef TEST
708     default:
709       panic ("Unknown var type %d", var->var_flags);
710 #endif
711     }
712 }
713 
714 static void
write_mp_windows(fp)715 write_mp_windows (fp)
716      FILE *fp;
717 {
718   struct line line;
719   line.alloc = 0;
720   line.buf = 0;
721   io_write_window_config (&line);
722   fputs (line.buf, fp);
723   free (line.buf);
724 }
725 
726 void
sylk_write_file(fp,rng)727 sylk_write_file (fp, rng)
728      FILE *fp;
729      struct rng *rng;
730 {
731   CELLREF r, c;
732   CELL *cp;
733   CELLREF crow = 0, ccol = 0;
734   unsigned short w;
735 
736   /* struct var *var; */
737   int old_a0;
738 
739   (void) fprintf (fp, "ID;POLEO\n");
740 
741   /* If no range given, write the entire file */
742   if (!rng)
743     {
744       int n;
745       int fmts;
746       char *data[9];
747 
748       rng = &all_rng;
749 
750       (void) fprintf (fp, "F;D%s%c%u\n", sylk_fmt_to_str (default_fmt, default_prc),
751 		      jst_to_chr (default_jst), default_width);
752 
753       fmts = usr_set_fmts ();
754       for (n = 0; n < 16; n++)
755 	{
756 	  if (fmts & (1 << n))
757 	    {
758 	      get_usr_stats (n, data);
759 	      fprintf (fp, "U;N%u;P%s;S%s", n + 1, data[7], data[8]);
760 	      if (data[0][0])
761 		fprintf (fp, ";HP%s", data[0]);
762 	      if (data[1][0])
763 		fprintf (fp, ";HN%s", data[1]);
764 	      if (data[2][0])
765 		fprintf (fp, ";TP%s", data[2]);
766 	      if (data[3][0])
767 		fprintf (fp, ";TN%s", data[3]);
768 	      if (data[4][0])
769 		fprintf (fp, ";Z%s", data[4]);
770 	      if (data[5][0])
771 		fprintf (fp, ";C%s", data[5]);
772 	      if (data[6])
773 		fprintf (fp, ";D%s", data[6]);
774 	      putc ('\n', fp);
775 	    }
776 	}
777       write_mp_options (fp);
778 
779       (void) fprintf (fp, "B;Y%u;X%u\n", highest_row (), highest_col ());
780 
781     }
782 
783   old_a0 = Global->a0;
784   Global->a0 = Global->sylk_a0;
785 
786   find_widths (rng->lc, rng->hc);
787   w = next_width (&c);
788   while (w)
789     {
790       CELLREF cc, ccc;
791       unsigned short ww;
792       cc = c;
793       do
794 	ww = next_width (&ccc);
795       while (ccc == ++cc && ww == w);
796       (void) fprintf (fp, "F;W%u %u %u\n", c, cc - 1, w - 1);
797       c = ccc;
798       w = ww;
799     }
800 
801   find_heights (rng->lr, rng->hr);
802   w = next_height (&c);
803   while (w)
804     {
805       CELLREF rr, rrr;
806       unsigned short ww;
807 
808       rr = r;
809       do
810 	ww = next_height (&rrr);
811       while (rrr == ++rr && ww == w);
812       (void) fprintf (fp, "F;H%u %u %u\n", r, rr - 1, w - 1);
813       r = rrr;
814       w = ww;
815     }
816 
817   sylk_fp = fp;
818   sylk_rng = rng;
819   for_all_vars (sylk_write_var);
820   find_cells_in_range (rng);
821   while ((cp = next_row_col_in_range (&r, &c)))
822     {
823       char *ptr;
824       int f1, j1;
825       char p_buf[40];
826 
827       f1 = GET_FORMAT (cp);
828       j1 = GET_JST (cp);
829       if (f1 != FMT_DEF || j1 != JST_DEF)
830 	{
831 	  (void) fprintf (fp, "F;");
832 	  if (c != ccol)
833 	    {
834 	      (void) fprintf (fp, "X%u;", c);
835 	      ccol = c;
836 	    }
837 	  if (r != crow)
838 	    {
839 	      (void) fprintf (fp, "Y%u;", r);
840 	      crow = r;
841 	    }
842 	  (void) fprintf (fp, "F%s%c\n", sylk_fmt_to_str (f1, GET_PRECISION(cp)), jst_to_chr (j1));
843 	}
844 
845       if (!GET_TYP (cp) && !cp->cell_formula)
846 	continue;
847 
848       (void) fprintf (fp, "C;");
849       if (c != ccol)
850 	{
851 	  (void) fprintf (fp, "X%u;", c);
852 	  ccol = c;
853 	}
854       if (r != crow)
855 	{
856 	  (void) fprintf (fp, "Y%u;", r);
857 	  crow = r;
858 	}
859 
860       if (cp->cell_formula)
861 	{
862 	  (void) fprintf (fp, "E%s", decomp (r, c, cp));
863 	  decomp_free ();
864 	}
865 
866       switch (GET_TYP (cp))
867 	{
868 	case 0:
869 	  ptr = 0;
870 	  break;
871 	case TYP_STR:
872 	  ptr = 0;
873 	  if (cp->cell_formula)
874 	    putc (';', fp);
875 	  (void) fprintf (fp, "K\"%s\"", cp->cell_str);
876 	  break;
877 	case TYP_FLT:
878 	  ptr = flt_to_str (cp->cell_flt);
879 	  break;
880 	case TYP_INT:
881 	  sprintf (p_buf, "%ld", cp->cell_int);
882 	  ptr = p_buf;
883 	  break;
884 	case TYP_BOL:
885 	  ptr = bname[cp->cell_bol];
886 	  break;
887 	case TYP_ERR:
888 	  ptr = ename[cp->cell_err];
889 	  break;
890 	default:
891 	  ptr = 0;
892 #ifdef TEST
893 	  panic ("What cell type %d", GET_TYP (cp));
894 #endif
895 	}
896 
897       if (ptr)
898 	{
899 	  if (cp->cell_formula)
900 	    putc (';', fp);
901 	  (void) fprintf (fp, "K%s", ptr);
902 	}
903       if (GET_LCK (cp) == LCK_LCK)
904 	(void) fprintf (fp, ";P");
905 
906       putc ('\n', fp);
907     }
908 
909   if (rng == &all_rng)
910     write_mp_windows (fp);
911 
912   (void) fprintf (fp, "E\n");
913   Global->a0 = old_a0;
914 }
915 
916 int
sylk_set_options(set_opt,option)917 sylk_set_options (set_opt, option)
918      int set_opt;
919      char *option;
920 {
921   return -1;
922 }
923 
924 void
sylk_show_options()925 sylk_show_options ()
926 {
927   io_text_line ("File format: sylk  (Microsoft Multiplan interchange format)");
928 }
929 
930 /*
931  * Function mapping table
932  *
933  * This table contains the conversion from SYLK to Oleo functions.
934  *
935  * FIX ME this is far from complete
936  */
937 static struct {
938 	char	*sylk,
939 		*oleo;
940 } sylk2oleo [] = {
941 	{ "SUM",		"sum"	},
942 	{ "PRODUCT",		"prod"	},
943 	{ "AVERAGE",		"avg"	},
944 	{ "AVERAGE",		"std"	},
945 	{ "MAX",		"max"	},
946 	{ "MIN",		"min"	},
947 	{ "COUNT",		"count"	},
948 	{ "COUNT",		"var"	},
949 	{ "ABS",		"abs"	},
950 	{ NULL,			NULL	}
951 };
952 
953 static char *
sylk_to_oleo_functions(char * f)954 sylk_to_oleo_functions(char *f)
955 {
956 	static char	buf[SYLK_LEN];
957 	char		*p, *q;
958 	int		i, j, done;
959 
960 	for (p=f, q=buf; *p; p++) {
961 		done = 0;
962 		for (i=0; sylk2oleo[i].sylk; i++) {
963 			int	ls = strlen(sylk2oleo[i].sylk);
964 			if (strncmp(p, sylk2oleo[i].sylk, ls) == 0) {
965 				int	lo = strlen(sylk2oleo[i].oleo);
966 
967 				p += ls - 1;
968 				for (j=0; j<lo; j++)
969 					*(q++) = sylk2oleo[i].oleo[j];
970 				done = 1;
971 				break;
972 			}
973 		}
974 
975 		if (! done)
976 			*(q++) = *p;
977 		*q = '\0';
978 	}
979 #if 0
980 	fprintf(stderr, "Sylk2Oleo(%s) -> '%s'\n", f, buf);
981 #endif
982 	return buf;
983 }
984