stf.c 7.51 KB
Newer Older
1 2
/*
 * stf.c : reads sheets using CSV/Fixed encoding while allowing
3
 *         fine-tuning of the import process
4
 *
5
 * Copyright (C) Almer. S. Tigelaar.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 * EMail: almer1@dds.nl or almer-t@bigfoot.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <gnome.h>
#include <glade/glade.h>

#include "plugin.h"
#include "gnumeric.h"
#include "file.h"
39
#include "style.h"
40
#include "formats.h"
41
#include "workbook.h"
Jon K Hellan's avatar
Jon K Hellan committed
42
#include "command-context.h"
43 44 45 46 47 48 49 50 51 52

#include "stf.h"
#include "dialog-stf.h"

#include "stf-separated.h"
#include "stf-fixed.h"

#define STF_NO_FILE_DESCRIPTOR -1
#define STF_NO_DATA 0

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
/**
 * stf_is_valid_input
 * @src : a structure containing file information
 *
 * returns weather the input data is valid to import.
 * (meaning it checks weather it is text only)
 *
 * returns : true if valid, false otherwise
 **/
static gboolean
stf_is_valid_input (FileSource_t *src)
{
	gboolean valid;
	const char *iterator = src->data;

	if (src->len == 0)
		return FALSE;

	valid = TRUE;
	while (*iterator) {
		if (!isprint (*iterator) && *iterator != '\n' && *iterator != '\t') {
			valid = FALSE;
			break;
		}
		iterator++;
78 79
	}

80 81
	return valid;
}
82 83 84 85 86

/**
 * stf_convert_to_unix
 * @src : a structure containing file information
 *
87
 * This function will convert the @src->data into a
88 89
 * unix line-terminated format. this means that CRLF (windows) will be converted to LF
 * and CR (Macintosh) to LF
90
 * NOTE : This will not resize the buffer
91
 *
92 93 94 95 96 97
 * returns : TRUE on success, FALSE otherwise.
 **/
static gboolean
stf_convert_to_unix (FileSource_t *src)
{
	const char *iterator = src->data;
98 99
	char *dest = (char *) src->data;
	int len = 0;
100 101 102

	if (iterator == STF_NO_DATA)
		return FALSE;
103

104
	while (*iterator) {
105

106 107 108
		if (*iterator == '\r') {
			const char *temp = iterator;
			temp++;
109

110
			if (*temp != '\n') {
111 112 113
				*dest = '\n';
				dest++;
				len++;
114 115 116 117
			}
			iterator++;
		}

118
		*dest = *iterator;
119

120
		iterator++;
121 122
		dest++;
		len++;
123
	}
124
	*dest = '\0';
125

126 127 128 129 130 131
	return TRUE;
}

/**
 * get_lines_count
 * @text : a multi-line string
132
 *
133 134
 * returns : the number of lines the string consists of
 **/
135
static unsigned long
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
stf_get_lines_count (const char *text)
{
	const char *cur = text-1;
	int linecount = 0;

	do {
		cur++;
		while (*cur != '\n' && *cur != '\r'&& *cur != 0) {
			cur++;
		}
		linecount++;
	} while (*cur != 0);

	return linecount;

}


/**
155 156
 * stf_close_and_free
 * @src : struct containing information about the file to close&buffer to free
157
 *
158
 * Will close the file in @src and will free the buffer in @src
159 160 161 162
 *
 * returns : nothing
 **/
static void
163
stf_close_and_free (FileSource_t *src)
164 165
{
	if (src->data != STF_NO_DATA) {
166
		g_free ( (char *) src->data);
167 168 169 170 171 172 173 174 175 176
		src->data = STF_NO_DATA;
	}

	if (src->fd != STF_NO_FILE_DESCRIPTOR) {
		close (src->fd);
		src->fd = STF_NO_FILE_DESCRIPTOR;
	}
}

/**
177 178
 * stf_open_and_read
 * @filename : name of the file to open&read
179
 * @src : struct to store the file information/memory locations in
180
 *
181 182
 * Will open filename, read the file into a g_alloced memory buffer
 * and fill a FileSource_t structure accordingly
183
 *
184
 * returns : true if successfully opend and read, false otherwise.
185 186
 **/
static gboolean
187
stf_open_and_read (FileSource_t *src)
188 189
{
	struct stat sbuf;
190
	const char *data;
191 192 193 194 195 196 197 198 199 200 201
	int const fd = open (src->filename, O_RDONLY);

	if (fd < 0)
		return FALSE;

	if (fstat(fd, &sbuf) < 0) {
		close (fd);
		return FALSE;
	}


202
	data = g_malloc0 (sbuf.st_size + 1);
203

204
	if (read (fd, (char *) data, sbuf.st_size) != sbuf.st_size)
205
		return FALSE;
206

207 208 209
	src->len  = sbuf.st_size;
	src->data = data;
	src->cur  = data;
210 211 212 213 214 215

	return TRUE;
}

/**
 * stf_read_workbook
Jon K Hellan's avatar
Jon K Hellan committed
216 217
 * @context  : command context
 * @book     : workbook
218 219 220 221 222 223
 * @filename : file to read from+convert
 *
 * Main routine, handles importing a file including all dialog mumbo-jumbo
 *
 * returns : NULL on success or an error message otherwise
 **/
Jon K Hellan's avatar
Jon K Hellan committed
224 225 226
static int
stf_read_workbook (CommandContext *context, Workbook *book,
		   char const *filename)
227
{
228
	FileSource_t src;
Jon K Hellan's avatar
Jon K Hellan committed
229
	int result = 0;
230 231 232 233 234 235 236 237 238 239
	char *name = g_strdup_printf (_("Imported %s"), g_basename (filename));
	src.sheet  = sheet_new (book, name);
	g_free (name);

	src.fd = STF_NO_FILE_DESCRIPTOR;
	src.data = STF_NO_DATA;
	src.filename = filename;
	src.rowcount = 0;
	src.colcount = 0;

240 241
	if (!stf_open_and_read (&src)) {
		stf_close_and_free (&src);
Jon K Hellan's avatar
Jon K Hellan committed
242 243 244
		gnumeric_error_read
			(context, _("Error while trying to memory map file"));
		return -1;
245
	}
246 247 248

	if (!stf_convert_to_unix (&src)) {
		stf_close_and_free (&src);
Jon K Hellan's avatar
Jon K Hellan committed
249 250 251 252
		gnumeric_error_read
			(context, _("Error while trying to pre-convert file"));
		return -1;
	}
253 254 255

	if (!stf_is_valid_input (&src)) {
		stf_close_and_free (&src);
Jon K Hellan's avatar
Jon K Hellan committed
256 257 258 259
		gnumeric_error_read
			(context,
			_("This file does not seem to be a valid text file"));
		return -1;
260
	}
261

262 263
	src.totallines = stf_get_lines_count (src.data);
	src.lines      = src.totallines;
264

265
	workbook_attach_sheet (book, src.sheet);
266

Jon K Hellan's avatar
Jon K Hellan committed
267
	result = dialog_stf (context, &src);
268

Jon K Hellan's avatar
Jon K Hellan committed
269
	if (result == 0) {
270 271 272
		Range range = sheet_get_extent (src.sheet);

		sheet_style_optimize (src.sheet, range);
Jody Goldberg's avatar
Jody Goldberg committed
273
		sheet_range_calc_spans (src.sheet, range, SPANCALC_RENDER);
274
	} else
275 276
		workbook_detach_sheet (book, src.sheet, TRUE);

277
	stf_close_and_free (&src);
278

Jon K Hellan's avatar
Jon K Hellan committed
279
	return (result);
280 281 282 283 284 285 286
}

/**
 * stf_can_unload
 * @pd: a plugin data struct
 *
 * returns weather the plugin can unload, something which can be always
287
 * done currently as this plug-in does not keep dynamically allocated
288 289 290 291 292 293 294 295 296 297 298 299 300 301
 * stuff in memory all the time.
 *
 * returns : always TRUE
 **/
static int
stf_can_unload (PluginData *pd)
{
	/* We can always unload */
	return TRUE;
}

/**
 * stf_cleanup_plugin
 * @pd: a plugin data struct
302
 *
303 304 305 306 307 308 309 310 311
 * returns : nothing
 **/
static void
stf_cleanup_plugin (PluginData *pd)
{
	file_format_unregister_open (NULL, stf_read_workbook);
/*	file_format_unregister_save (stf_write_workbook); */
}

312 313 314
#define STF_TITLE _("Structured Text File (STF) module")
#define STF_DESCR _("This plugin reads sheets using CSV/Fixed encoding while allowing fine-tuning of the import process")

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
/**
 * init_plugin
 * @pd : a plugin data struct
 *
 * Registers some plug-in related things like the name displayed
 * in the plug-in manager and the name in the import screen and
 * "connects" some callback routines
 *
 * returns : PLUGIN_OK normally or PLUGIN_QUIET_ERROR on a version mismatch
 **/
PluginInitResult
init_plugin (CommandContext *context, PluginData *pd)
{
	char *desc;

	if (plugin_version_mismatch  (context, pd, GNUMERIC_VERSION))
		return PLUGIN_QUIET_ERROR;
332

5's avatar
5 committed
333
	desc = _("Text File import");
334 335 336
	file_format_register_open (1, desc, NULL, stf_read_workbook);

	/*    desc = _("Structured Text File format (*.stf)");
337
	      file_format_register_save (".stf", desc, stf_write_workbook); */
338

339 340 341 342 343 344 345 346
	if (plugin_data_init (pd, stf_can_unload, stf_cleanup_plugin,
			      STF_TITLE, STF_DESCR))
	  {
	          glade_gnome_init();
		  return PLUGIN_OK;
	  }
	else
	          return PLUGIN_ERROR;
347 348

}