Commit ca8fd19d authored by Morten Welinder's avatar Morten Welinder

Make fn-string a plugin.

parent a7981d9d
......@@ -37,7 +37,9 @@ Morten:
* Plug gigantic leak in parser.
* Improve gnumeric-expr-entry's utf8 support and speed.
* Plug various little leaks.
* Make fn-date a plugin.
* Make fn-eng a plugin.
* Make fn-string a plugin.
--------------------------------------------------------------------------
Gnumeric 1.1.2
......
......@@ -644,6 +644,7 @@ plugins/Makefile
plugins/numtheory/Makefile
plugins/fn-date/Makefile
plugins/fn-eng/Makefile
plugins/fn-string/Makefile
plugins/derivatives/Makefile
plugins/sc/Makefile
plugins/sylk/Makefile
......
......@@ -51,7 +51,7 @@ endif
SUBDIRS_FILE_FORMATS = $(EXCEL_DIR) lotus-123 oleo sc sylk xbase html dif \
xml_sax applix mps $(GNOME_GLOSSARY_DIR) $(PSICONV_DIR)
SUBDIRS_FUNCTIONS = numtheory fn-date fn-eng derivatives \
SUBDIRS_FUNCTIONS = numtheory fn-date fn-eng fn-string derivatives \
$(PYTHON_DIR) $(PERL_DIR) $(GUILE_DIR) $(GB_DIR) \
$(GDA_DIR) $(PYFUNC_DIR)
......
Makefile.in
Makefile
.deps
.libs
*.lo
*.la
plugin.xml
INCLUDES = \
-DGNOMELOCALEDIR=\""$(datadir)/locale"\" \
-I$(top_srcdir)/src -I$(top_builddir)/src \
$(GNUMERIC_CFLAGS)
gnumeric_plugin_fnstringdir = $(gnumeric_plugindir)/fn-string
xmldir = $(gnumeric_plugin_fnstringdir)
gnumeric_plugin_fnstring_LTLIBRARIES = plugin.la
plugin_la_LDFLAGS = -module -avoid-version
plugin_la_SOURCES = functions.c
xml_in_files = plugin.xml.in
xml_DATA = $(xml_in_files:.xml.in=.xml)
@INTLTOOL_XML_RULE@
EXTRA_DIST = $(xml_in_files)
DISTCLEANFILES = $(xml_DATA)
<?xml version="1.0"?>
<plugin id="Gnumeric_fnstring">
<information>
<_name>String Functions</_name>
<_description>Functions for manipulating strings</_description>
</information>
<loader type="g_module">
<attribute name="module_file" value="plugin.la"/>
</loader>
<services>
<service type="function_group" id="string">
<_category>String</_category>
<functions>
<function name="char"/>
<function name="clean"/>
<function name="code"/>
<function name="concatenate"/>
<function name="dollar"/>
<function name="exact"/>
<function name="find"/>
<function name="fixed"/>
<function name="left"/>
<function name="len"/>
<function name="lower"/>
<function name="proper"/>
<function name="mid"/>
<function name="replace"/>
<function name="rept"/>
<function name="right"/>
<function name="search"/>
<function name="substitute"/>
<function name="t"/>
<function name="text"/>
<function name="trim"/>
<function name="upper"/>
<function name="value"/>
</functions>
</service>
</services>
</plugin>
......@@ -17,6 +17,8 @@ plugins/fn-date/functions.c
plugins/fn-date/plugin.xml.in
plugins/fn-eng/functions.c
plugins/fn-eng/plugin.xml.in
plugins/fn-string/functions.c
plugins/fn-string/plugin.xml.in
plugins/gb/plugin.c
plugins/gb/plugin.xml.in
plugins/gda/plugin-gda.c
......@@ -179,7 +181,6 @@ src/functions/fn-lookup.c
src/functions/fn-math.c
src/functions/fn-sheet.c
src/functions/fn-stat.c
src/functions/fn-string.c
src/gnumeric-canvas.c
src/gnumeric-graph.c
src/gnumeric-pane.c
......
......@@ -37,7 +37,6 @@ static SymbolTable *global_symbol_table = NULL;
extern void math_functions_init (void);
extern void sheet_functions_init (void);
extern void string_functions_init (void);
extern void stat_functions_init (void);
extern void finance_functions_init (void);
extern void lookup_functions_init (void);
......@@ -52,7 +51,6 @@ functions_init (void)
math_functions_init ();
sheet_functions_init ();
string_functions_init ();
stat_functions_init ();
finance_functions_init ();
lookup_functions_init ();
......
......@@ -15,5 +15,4 @@ libfunctions_a_SOURCES = \
fn-lookup.c \
fn-math.c \
fn-sheet.c \
fn-stat.c \
fn-string.c
fn-stat.c
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* fn-date.c: Built in date functions.
*
* Authors:
* Miguel de Icaza (miguel@gnu.org)
* Jukka-Pekka Iivonen (iivonen@iki.fi)
*
* 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 <gnumeric-config.h>
#include <gnumeric.h>
#include <func.h>
#include <parse-util.h>
#include <str.h>
#include <cell.h>
#include <datetime.h>
#include <value.h>
#include <auto-format.h>
#include <mathfunc.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <libgnome/gnome-i18n.h>
#define DAY_SECONDS (3600*24)
/***************************************************************************/
static const char *help_date = {
N_("@FUNCTION=DATE\n"
"@SYNTAX=DATE (year,month,day)\n"
"@DESCRIPTION="
"DATE returns the number of days since the 1st of january of 1900"
"(the date serial number) for the given year, month and day.\n"
"\n"
"If @month < 1 or @month > 12, the year will be corrected. A "
"similar correction takes place for days.\n"
"\n"
"The @years should be at least 1900. If "
"@years < 1900, it is assumed to be 1900 + @years.\n"
"\n"
"If the given date is not valid, DATE returns #NUM! error.\n"
"This function is Excel compatible."
"\n"
"@EXAMPLES=\n"
"DATE(2001, 3, 30) returns 'Mar 30, 2001'.\n "
"\n"
"@SEEALSO=TODAY, NOW")
};
static Value *
gnumeric_date (FunctionEvalInfo *ei, Value **argv)
{
int year, month, day;
GDate date;
year = value_get_as_int (argv [0]);
month = value_get_as_int (argv [1]);
day = value_get_as_int (argv [2]);
if (year < 0 || year > 9999)
goto error;
if (year < 1900) /* 1900, not 100. Ick! */
year += 1900;
g_date_clear (&date, 1);
g_date_set_dmy (&date, 1, 1, year);
if (!g_date_valid (&date))
goto error;
if (month > 0)
g_date_add_months (&date, month - 1);
else
g_date_subtract_months (&date, 1 - month);
if (!g_date_valid (&date))
goto error;
if (day > 0)
g_date_add_days (&date, day - 1);
else
g_date_subtract_days (&date, 1 - day);
if (!g_date_valid (&date))
goto error;
if (g_date_year (&date) < 1900 || g_date_year (&date) >= 11900)
goto error;
return value_new_int (datetime_g_to_serial (&date));
error:
return value_new_error (ei->pos, gnumeric_err_NUM);
}
/***************************************************************************/
static const char *help_unix2date = {
N_("@FUNCTION=UNIX2DATE\n"
"@SYNTAX=UNIX2DATE(unixtime)\n"
"@DESCRIPTION="
"UNIX2DATE converts a unix time into a spreadsheet date and time.\n"
"\n"
"A unix time is the number of seconds since midnight January 1, 1970.\n"
"\n"
"@EXAMPLES=\n"
"\n"
"@SEEALSO=NOW, DATE, DATE2UNIX")
};
static Value *
gnumeric_unix2date (FunctionEvalInfo *ei, Value **argv)
{
gnum_float futime = value_get_as_float (argv [0]);
time_t utime = (time_t)futime;
/* Check for overflow. */
if (gnumabs (futime - utime) >= 1.0)
return value_new_error (ei->pos, gnumeric_err_VALUE);
return value_new_float (datetime_timet_to_serial_raw (utime) +
(futime - utime));
}
/***************************************************************************/
static const char *help_date2unix = {
N_("@FUNCTION=DATE2UNIX\n"
"@SYNTAX=DATE2UNIX(serial)\n"
"@DESCRIPTION="
"DATE2UNIX converts a spreadsheet date and time serial number "
"into a unix time.\n"
"\n"
"A unix time is the number of seconds since midnight January 1, 1970.\n"
"\n"
"@EXAMPLES=\n"
"\n"
"@SEEALSO=NOW, DATE, UNIX2DATE")
};
static Value *
gnumeric_date2unix (FunctionEvalInfo *ei, Value **argv)
{
gnum_float fserial = value_get_as_float (argv [0]);
int serial = (int)fserial;
time_t utime = datetime_serial_to_timet (serial);
/* Check for overflow. */
if (gnumabs (fserial - serial) >= 1.0 || utime == (time_t)-1)
return value_new_error (ei->pos, gnumeric_err_VALUE);
return value_new_int (utime +
gnumeric_fake_round (DAY_SECONDS * (fserial - serial)));
}
/***************************************************************************/
static const char *help_datevalue = {
N_("@FUNCTION=DATEVALUE\n"
"@SYNTAX=DATEVALUE(date_str)\n"
"@DESCRIPTION="
"DATEVALUE returns the serial number of the date. @date_str is "
"the string that contains the date.\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"DATEVALUE(\"1/1/1999\") equals 36161."
"\n"
"@SEEALSO=DATE")
};
static Value *
gnumeric_datevalue (FunctionEvalInfo *ei, Value **argv)
{
if (argv[0]->type == VALUE_ERROR)
return value_duplicate (argv[0]);
return value_new_int (datetime_value_to_serial (argv[0]));
}
/***************************************************************************/
static const char *help_datedif = {
N_("@FUNCTION=DATEDIF\n"
"@SYNTAX=DATEDIF(date1,date2,interval)\n"
"@DESCRIPTION="
"DATEDIF returns the difference between two dates. @interval is "
"one of six possible values: \"y\", \"m\", \"d\", \"ym\", "
"\"md\", and \"yd\".\n"
"The first three options will return the "
"number of complete years, months, or days, respectively, between "
"the two dates specified.\n"
"\"ym\" will return the number of full months between the two "
"dates, not including the difference in years.\n"
"\"md\" will return the number of full days between the two "
"dates, not including the difference in months.\n"
"\"yd\" will return the number of full days between the two "
"dates, not including the difference in years.\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"DATEDIF(DATE(2000,4,30),DATE(2003,8,4),\"d\") equals 1191.\n"
"DATEDIF(DATE(2000,4,30),DATE(2003,8,4),\"y\") equals 3.\n"
"\n"
"@SEEALSO=DATE")
};
static int
datedif_opt_ym (GDate *gdate1, GDate *gdate2)
{
g_assert (g_date_valid (gdate1));
g_assert (g_date_valid (gdate2));
return datetime_g_months_between (gdate1, gdate2) % 12;
}
static int
datedif_opt_yd (GDate *gdate1, GDate *gdate2, int excel_compat)
{
int day;
g_assert (g_date_valid (gdate1));
g_assert (g_date_valid (gdate2));
day = g_date_day (gdate1);
g_date_add_years (gdate1,
datetime_g_years_between (gdate1, gdate2));
/* according to glib.h, feb 29 turns to feb 28 if necessary */
if (excel_compat) {
int new_year1, new_year2;
/* treat all years divisible by four as leap years: */
/* this is clearly wrong, but it's what Excel does. */
/* (I use 2004 here since it is clearly a leap year.) */
new_year1 = 2004 + (g_date_year (gdate1) & 0x3);
new_year2 = new_year1 + (g_date_year (gdate2) -
g_date_year (gdate1));
g_date_set_year (gdate1, new_year1);
g_date_set_year (gdate2, new_year2);
{
static gboolean need_warning = TRUE;
if (need_warning) {
g_warning("datedif is known to differ from Excel "
"for some values.");
need_warning = FALSE;
}
}
}
return datetime_g_days_between (gdate1, gdate2);
}
static int
datedif_opt_md (GDate *gdate1, GDate *gdate2, int excel_compat)
{
int day;
g_assert (g_date_valid (gdate1));
g_assert (g_date_valid (gdate2));
day = g_date_day (gdate1);
g_date_add_months (gdate1,
datetime_g_months_between (gdate1, gdate2));
/* according to glib.h, days>28 decrease if necessary */
if (excel_compat) {
int new_year1, new_year2;
/* treat all years divisible by four as leap years: */
/* this is clearly wrong, but it's what Excel does. */
/* (I use 2004 here since it is clearly a leap year.) */
new_year1 = 2004 + (g_date_year (gdate1) & 0x3);
new_year2 = new_year1 + (g_date_year (gdate2) -
g_date_year (gdate1));
g_date_set_year (gdate1, new_year1);
g_date_set_year (gdate2, new_year2);
/* add back the days if they were decreased by
g_date_add_months */
/* ( i feel this is inferior because it reports e.g.:
datedif(1/31/95,3/1/95,"d") == -2 ) */
g_date_add_days (gdate1,
day - g_date_day (gdate1));
}
return datetime_g_days_between (gdate1, gdate2);
}
static Value *
gnumeric_datedif (FunctionEvalInfo *ei, Value **argv)
{
int date1, date2;
const char *opt;
GDate *gdate1, *gdate2;
Value *result;
date1 = floor (value_get_as_float (argv [0]));
date2 = floor (value_get_as_float (argv [1]));
opt = value_peek_string (argv[2]);
if (date1 > date2) {
return value_new_error (ei->pos, gnumeric_err_NUM);
}
if (!strcmp (opt, "d")) {
return value_new_int (date2 - date1);
}
gdate1 = datetime_serial_to_g (date1);
gdate2 = datetime_serial_to_g (date2);
if (!g_date_valid (gdate1) || !g_date_valid (gdate2)) {
result = value_new_error (ei->pos, gnumeric_err_VALUE);
} else {
if (!strcmp (opt, "m")) {
result = value_new_int (
datetime_g_months_between (gdate1, gdate2));
} else if (!strcmp (opt, "y")) {
result = value_new_int (
datetime_g_years_between (gdate1, gdate2));
} else if (!strcmp (opt, "ym")) {
result = value_new_int (
datedif_opt_ym (gdate1, gdate2));
} else if (!strcmp (opt, "yd")) {
result = value_new_int (
datedif_opt_yd (gdate1, gdate2, 1));
} else if (!strcmp (opt, "md")) {
result = value_new_int (
datedif_opt_md (gdate1, gdate2, 1));
} else {
result = value_new_error (
ei->pos, gnumeric_err_VALUE);
}
}
datetime_g_free (gdate1);
datetime_g_free (gdate2);
return result;
}
/***************************************************************************/
static const char *help_edate = {
N_("@FUNCTION=EDATE\n"
"@SYNTAX=EDATE(date,months)\n"
"@DESCRIPTION="
"EDATE returns the serial number of the date that is the "
"specified number of months before or after a given date. "
"@date is the serial number of the initial date and @months "
"is the number of months before (negative number) or after "
"(positive number) the initial date.\n"
"This function is Excel compatible. "
"\n"
"If @months is not an integer, it is truncated."
"\n"
"@EXAMPLES=\n"
"EDATE(DATE(2001,12,30),2) returns 'Feb 28, 2002'.\n"
"\n"
"@SEEALSO=DATE")
};
static Value *
gnumeric_edate (FunctionEvalInfo *ei, Value **argv)
{
int serial, months;
GDate* date;
Value *res;
serial = value_get_as_int(argv[0]);
months = value_get_as_int(argv[1]);
date = datetime_serial_to_g (serial);
if (!g_date_valid (date)) {
datetime_g_free (date);
return value_new_error (ei->pos, gnumeric_err_VALUE);
}
if (months > 0)
g_date_add_months (date, months);
else
g_date_subtract_months (date, -months);
if (!g_date_valid (date)) {
datetime_g_free (date);
return value_new_error (ei->pos, gnumeric_err_NUM);
}
res = value_new_int (datetime_g_to_serial (date));
datetime_g_free (date);
return res;
}
/***************************************************************************/
static const char *help_today = {
N_("@FUNCTION=TODAY\n"
"@SYNTAX=TODAY()\n"
"@DESCRIPTION="
"TODAY returns the serial number for today (the number of days "
"elapsed since the 1st of January of 1900).\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"TODAY() returns 'Nov 6, 2001' on that particular day.\n "
"\n"
"@SEEALSO=NOW")
};
static Value *
gnumeric_today (FunctionEvalInfo *ei, Value **argv)
{
return value_new_int (datetime_timet_to_serial (time (NULL)));
}
/***************************************************************************/
static const char *help_now = {
N_("@FUNCTION=NOW\n"
"@SYNTAX=NOW ()\n"
"@DESCRIPTION="
"NOW returns the serial number for the date and time at the time "
"it is evaluated.\n"
""
"Serial Numbers in Gnumeric are represented as follows:"
"The integral part is the number of days since the 1st of "
"January of 1900. The decimal part represent the fraction "
"of the day and is mapped into hour, minutes and seconds.\n"
""
"For example: .0 represents the beginning of the day, and 0.5 "
"represents noon.\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"NOW().\n"
"\n"
"@SEEALSO=TODAY")
};
static Value *
gnumeric_now (FunctionEvalInfo *ei, Value **argv)
{
return value_new_float (datetime_timet_to_serial_raw (time (NULL)));
}
/***************************************************************************/
static const char *help_time = {
N_("@FUNCTION=TIME\n"
"@SYNTAX=TIME (hours,minutes,seconds)\n"
"@DESCRIPTION="
"TIME returns a fraction representing the time of day.\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"TIME(3, 5, 23) equals 3:05AM.\n"
"\n"
"@SEEALSO=HOUR")
};
static Value *
gnumeric_time (FunctionEvalInfo *ei, Value **argv)
{
gnum_float hours, minutes, seconds;
hours = value_get_as_float (argv [0]);
minutes = value_get_as_float (argv [1]);
seconds = value_get_as_float (argv [2]);
return value_new_float ((hours * 3600 + minutes * 60 + seconds) /
DAY_SECONDS);
}
/***************************************************************************/
static const char *help_timevalue = {
N_("@FUNCTION=TIMEVALUE\n"
"@SYNTAX=TIMEVALUE (timetext)\n"
"@DESCRIPTION="
"TIMEVALUE returns a fraction representing the time of day, a number "
"between 0 and 1.\n"
"This function is Excel compatible. "
"\n"
"@EXAMPLES=\n"
"TIMEVALUE(\"3:05\") equals 0.128472.\n"
"TIMEVALUE(\"2:24:53 PM\") equals 0.600613.\n"
"\n"
"@SEEALSO=HOUR,MINUTE")