3 ** Originally written by Steven M. Bellovin <smb@research.att.com> while
4 ** at the University of North Carolina at Chapel Hill. Later tweaked by
5 ** a couple of people on Usenet. Completely overhauled by Rich $alz
6 ** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
8 ** This grammar has 13 shift/reduce conflicts.
10 ** This code is in the public domain and has no copyright.
12 ** Since butchered by Mark Wooding, 1999-03-07.
19 #define ISSPACE(c) (isspace ((unsigned char)c))
20 #define ISALPHA(c) (isalpha ((unsigned char)c))
21 #define ISUPPER(c) (isupper ((unsigned char)c))
22 #define ISDIGIT_LOCALE(c) ((unsigned char)isdigit (c))
24 /* ISDIGIT differs from ISDIGIT_LOCALE, as follows:
25 - Its arg may be any int or unsigned int; it need not be an unsigned char.
26 - It's guaranteed to evaluate its argument exactly once.
27 - It's typically faster.
28 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
29 only '0' through '9' are digits. Prefer ISDIGIT to ISDIGIT_LOCALE unless
30 it's important to use the locale's definition of `digit' even when the
31 host does not conform to Posix. */
32 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
38 /* Some old versions of bison generate parsers that use bcopy.
39 That loses on systems that don't provide the function, so we have
40 to redefine it here. (Assume everyone has memcpy -- [mdw]) */
42 #define bcopy(from, to, len) memcpy ((to), (from), (len))
44 /* Remap normal yacc parser interface names (yyparse, yylex, yyerror, etc),
45 as well as gratuitiously global symbol names, so we can have multiple
46 yacc generated parsers in the same program. Note that these are only
47 the variables produced by yacc. If other parser generators (bison,
48 byacc, etc) produce additional global names that conflict at link time,
49 then those parser generators need to be fixed instead of adding those
50 names to this list. */
52 #define yymaxdepth gd_maxdepth
53 #define yyparse gd_parse
55 #define yyerror gd_error
56 #define yylval gd_lval
57 #define yychar gd_char
58 #define yydebug gd_debug
59 #define yypact gd_pact
66 #define yyexca gd_exca
67 #define yyerrflag gd_errflag
68 #define yynerrs gd_nerrs
73 #define yystate gd_state
78 #define yylloc gd_lloc
79 #define yyreds gd_reds /* With YYDEBUG defined */
80 #define yytoks gd_toks /* With YYDEBUG defined */
81 #define yylhs gd_yylhs
82 #define yylen gd_yylen
83 #define yydefred gd_yydefred
84 #define yydgoto gd_yydgoto
85 #define yysindex gd_yysindex
86 #define yyrindex gd_yyrindex
87 #define yygindex gd_yygindex
88 #define yytable gd_yytable
89 #define yycheck gd_yycheck
92 static int yyerror ();
95 #define HOUR(x) ((time_t)(x) * 60)
96 #define SECSPERDAY (24L * 60L * 60L)
98 #define MAX_BUFF_LEN 128 /* size of buffer to read the date into */
101 ** An entry in the lexical lookup table.
103 typedef struct _TABLE {
111 ** Meridian: am, pm, or 24-hour style.
113 typedef enum _MERIDIAN {
119 ** Global variables. We could get rid of most of these by using a good
120 ** union as the yacc stack. (This routine was originally written before
121 ** yacc had the %union construct.) Maybe someday; right now we only use
122 ** the %union very rarely.
124 static const char *yyInput;
125 static int yyDayOrdinal;
126 static int yyDayNumber;
127 static int yyHaveDate;
128 static int yyHaveDay;
129 static int yyHaveRel;
130 static int yyHaveTime;
131 static int yyHaveZone;
132 static int yyTimezone;
135 static int yyMinutes;
137 static int yySeconds;
139 static MERIDIAN yyMeridian;
141 static int yyRelHour;
142 static int yyRelMinutes;
143 static int yyRelMonth;
144 static int yyRelSeconds;
145 static int yyRelYear;
151 enum _MERIDIAN Meridian;
154 %token tAGO tDAY tDAY_UNIT tDAYZONE tDST tHOUR_UNIT tID
155 %token tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
156 %token tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
158 %type <Number> tDAY tDAY_UNIT tDAYZONE tHOUR_UNIT tMINUTE_UNIT
159 %type <Number> tMONTH tMONTH_UNIT
160 %type <Number> tSEC_UNIT tSNUMBER tUNUMBER tYEAR_UNIT tZONE
161 %type <Meridian> tMERIDIAN o_merid
187 time : tUNUMBER tMERIDIAN {
193 | tUNUMBER ':' tUNUMBER o_merid {
199 | tUNUMBER ':' tUNUMBER tSNUMBER {
205 ? -$4 % 100 + (-$4 / 100) * 60
206 : - ($4 % 100 + ($4 / 100) * 60));
208 | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
214 | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
221 ? -$6 % 100 + (-$6 / 100) * 60
222 : - ($6 % 100 + ($6 / 100) * 60));
230 yyTimezone = $1 - 60;
234 yyTimezone = $1 - 60;
252 date : tUNUMBER '/' tUNUMBER {
256 | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
257 /* Interpret as YYYY/MM/DD if $1 >= 1000, otherwise as MM/DD/YY.
258 The goal in recognizing YYYY/MM/DD is solely to support legacy
259 machine-generated dates like those in an RCS log listing. If
260 you want portability, use the ISO 8601 format. */
274 | tUNUMBER tSNUMBER tSNUMBER {
275 /* ISO 8601 format. yyyy-mm-dd. */
280 | tUNUMBER tMONTH tSNUMBER {
281 /* e.g. 17-JUN-1992. */
290 | tMONTH tUNUMBER ',' tUNUMBER {
299 | tUNUMBER tMONTH tUNUMBER {
307 yyRelSeconds = -yyRelSeconds;
308 yyRelMinutes = -yyRelMinutes;
309 yyRelHour = -yyRelHour;
310 yyRelDay = -yyRelDay;
311 yyRelMonth = -yyRelMonth;
312 yyRelYear = -yyRelYear;
317 relunit : tUNUMBER tYEAR_UNIT {
318 yyRelYear += $1 * $2;
320 | tSNUMBER tYEAR_UNIT {
321 yyRelYear += $1 * $2;
326 | tUNUMBER tMONTH_UNIT {
327 yyRelMonth += $1 * $2;
329 | tSNUMBER tMONTH_UNIT {
330 yyRelMonth += $1 * $2;
335 | tUNUMBER tDAY_UNIT {
338 | tSNUMBER tDAY_UNIT {
344 | tUNUMBER tHOUR_UNIT {
345 yyRelHour += $1 * $2;
347 | tSNUMBER tHOUR_UNIT {
348 yyRelHour += $1 * $2;
353 | tUNUMBER tMINUTE_UNIT {
354 yyRelMinutes += $1 * $2;
356 | tSNUMBER tMINUTE_UNIT {
357 yyRelMinutes += $1 * $2;
362 | tUNUMBER tSEC_UNIT {
363 yyRelSeconds += $1 * $2;
365 | tSNUMBER tSEC_UNIT {
366 yyRelSeconds += $1 * $2;
375 if (yyHaveTime && yyHaveDate && !yyHaveRel)
383 yyMonth= ($1/100)%100;
397 yyMinutes = $1 % 100;
418 /* Month and day table. */
419 static TABLE const MonthDayTable[] = {
420 { "january", tMONTH, 1 },
421 { "february", tMONTH, 2 },
422 { "march", tMONTH, 3 },
423 { "april", tMONTH, 4 },
424 { "may", tMONTH, 5 },
425 { "june", tMONTH, 6 },
426 { "july", tMONTH, 7 },
427 { "august", tMONTH, 8 },
428 { "september", tMONTH, 9 },
429 { "sept", tMONTH, 9 },
430 { "october", tMONTH, 10 },
431 { "november", tMONTH, 11 },
432 { "december", tMONTH, 12 },
433 { "sunday", tDAY, 0 },
434 { "monday", tDAY, 1 },
435 { "tuesday", tDAY, 2 },
437 { "wednesday", tDAY, 3 },
438 { "wednes", tDAY, 3 },
439 { "thursday", tDAY, 4 },
441 { "thurs", tDAY, 4 },
442 { "friday", tDAY, 5 },
443 { "saturday", tDAY, 6 },
447 /* Time units table. */
448 static TABLE const UnitsTable[] = {
449 { "year", tYEAR_UNIT, 1 },
450 { "month", tMONTH_UNIT, 1 },
451 { "fortnight", tDAY_UNIT, 14 },
452 { "week", tDAY_UNIT, 7 },
453 { "day", tDAY_UNIT, 1 },
454 { "hour", tHOUR_UNIT, 1 },
455 { "minute", tMINUTE_UNIT, 1 },
456 { "min", tMINUTE_UNIT, 1 },
457 { "second", tSEC_UNIT, 1 },
458 { "sec", tSEC_UNIT, 1 },
462 /* Assorted relative-time words. */
463 static TABLE const OtherTable[] = {
464 { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
465 { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
466 { "today", tMINUTE_UNIT, 0 },
467 { "now", tMINUTE_UNIT, 0 },
468 { "last", tUNUMBER, -1 },
469 { "this", tMINUTE_UNIT, 0 },
470 { "next", tUNUMBER, 1 /* Was bogusly 2 [mdw] */ },
471 { "first", tUNUMBER, 1 },
472 /* { "second", tUNUMBER, 2 }, */
473 { "third", tUNUMBER, 3 },
474 { "fourth", tUNUMBER, 4 },
475 { "fifth", tUNUMBER, 5 },
476 { "sixth", tUNUMBER, 6 },
477 { "seventh", tUNUMBER, 7 },
478 { "eighth", tUNUMBER, 8 },
479 { "ninth", tUNUMBER, 9 },
480 { "tenth", tUNUMBER, 10 },
481 { "eleventh", tUNUMBER, 11 },
482 { "twelfth", tUNUMBER, 12 },
487 /* The timezone table. */
488 static TABLE const TimezoneTable[] = {
489 { "gmt", tZONE, HOUR ( 0) }, /* Greenwich Mean */
490 { "ut", tZONE, HOUR ( 0) }, /* Universal (Coordinated) */
491 { "utc", tZONE, HOUR ( 0) },
492 { "wet", tZONE, HOUR ( 0) }, /* Western European */
493 { "bst", tDAYZONE, HOUR ( 0) }, /* British Summer */
494 { "wat", tZONE, HOUR ( 1) }, /* West Africa */
495 { "at", tZONE, HOUR ( 2) }, /* Azores */
497 /* For completeness. BST is also British Summer, and GST is
498 * also Guam Standard. */
499 { "bst", tZONE, HOUR ( 3) }, /* Brazil Standard */
500 { "gst", tZONE, HOUR ( 3) }, /* Greenland Standard */
503 { "nft", tZONE, HOUR (3.5) }, /* Newfoundland */
504 { "nst", tZONE, HOUR (3.5) }, /* Newfoundland Standard */
505 { "ndt", tDAYZONE, HOUR (3.5) }, /* Newfoundland Daylight */
507 { "ast", tZONE, HOUR ( 4) }, /* Atlantic Standard */
508 { "adt", tDAYZONE, HOUR ( 4) }, /* Atlantic Daylight */
509 { "est", tZONE, HOUR ( 5) }, /* Eastern Standard */
510 { "edt", tDAYZONE, HOUR ( 5) }, /* Eastern Daylight */
511 { "cst", tZONE, HOUR ( 6) }, /* Central Standard */
512 { "cdt", tDAYZONE, HOUR ( 6) }, /* Central Daylight */
513 { "mst", tZONE, HOUR ( 7) }, /* Mountain Standard */
514 { "mdt", tDAYZONE, HOUR ( 7) }, /* Mountain Daylight */
515 { "pst", tZONE, HOUR ( 8) }, /* Pacific Standard */
516 { "pdt", tDAYZONE, HOUR ( 8) }, /* Pacific Daylight */
517 { "yst", tZONE, HOUR ( 9) }, /* Yukon Standard */
518 { "ydt", tDAYZONE, HOUR ( 9) }, /* Yukon Daylight */
519 { "hst", tZONE, HOUR (10) }, /* Hawaii Standard */
520 { "hdt", tDAYZONE, HOUR (10) }, /* Hawaii Daylight */
521 { "cat", tZONE, HOUR (10) }, /* Central Alaska */
522 { "ahst", tZONE, HOUR (10) }, /* Alaska-Hawaii Standard */
523 { "nt", tZONE, HOUR (11) }, /* Nome */
524 { "idlw", tZONE, HOUR (12) }, /* International Date Line West */
525 { "cet", tZONE, -HOUR (1) }, /* Central European */
526 { "met", tZONE, -HOUR (1) }, /* Middle European */
527 { "mewt", tZONE, -HOUR (1) }, /* Middle European Winter */
528 { "mest", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
529 { "mesz", tDAYZONE, -HOUR (1) }, /* Middle European Summer */
530 { "swt", tZONE, -HOUR (1) }, /* Swedish Winter */
531 { "sst", tDAYZONE, -HOUR (1) }, /* Swedish Summer */
532 { "fwt", tZONE, -HOUR (1) }, /* French Winter */
533 { "fst", tDAYZONE, -HOUR (1) }, /* French Summer */
534 { "eet", tZONE, -HOUR (2) }, /* Eastern Europe, USSR Zone 1 */
535 { "bt", tZONE, -HOUR (3) }, /* Baghdad, USSR Zone 2 */
537 { "it", tZONE, -HOUR (3.5) },/* Iran */
539 { "zp4", tZONE, -HOUR (4) }, /* USSR Zone 3 */
540 { "zp5", tZONE, -HOUR (5) }, /* USSR Zone 4 */
542 { "ist", tZONE, -HOUR (5.5) },/* Indian Standard */
544 { "zp6", tZONE, -HOUR (6) }, /* USSR Zone 5 */
546 /* For completeness. NST is also Newfoundland Standard, and SST is
547 * also Swedish Summer. */
548 { "nst", tZONE, -HOUR (6.5) },/* North Sumatra */
549 { "sst", tZONE, -HOUR (7) }, /* South Sumatra, USSR Zone 6 */
551 { "wast", tZONE, -HOUR (7) }, /* West Australian Standard */
552 { "wadt", tDAYZONE, -HOUR (7) }, /* West Australian Daylight */
554 { "jt", tZONE, -HOUR (7.5) },/* Java (3pm in Cronusland!) */
556 { "cct", tZONE, -HOUR (8) }, /* China Coast, USSR Zone 7 */
557 { "jst", tZONE, -HOUR (9) }, /* Japan Standard, USSR Zone 8 */
559 { "cast", tZONE, -HOUR (9.5) },/* Central Australian Standard */
560 { "cadt", tDAYZONE, -HOUR (9.5) },/* Central Australian Daylight */
562 { "east", tZONE, -HOUR (10) }, /* Eastern Australian Standard */
563 { "eadt", tDAYZONE, -HOUR (10) }, /* Eastern Australian Daylight */
564 { "gst", tZONE, -HOUR (10) }, /* Guam Standard, USSR Zone 9 */
565 { "nzt", tZONE, -HOUR (12) }, /* New Zealand */
566 { "nzst", tZONE, -HOUR (12) }, /* New Zealand Standard */
567 { "nzdt", tDAYZONE, -HOUR (12) }, /* New Zealand Daylight */
568 { "idle", tZONE, -HOUR (12) }, /* International Date Line East */
572 /* Military timezone table. */
573 static TABLE const MilitaryTable[] = {
574 { "a", tZONE, HOUR ( 1) },
575 { "b", tZONE, HOUR ( 2) },
576 { "c", tZONE, HOUR ( 3) },
577 { "d", tZONE, HOUR ( 4) },
578 { "e", tZONE, HOUR ( 5) },
579 { "f", tZONE, HOUR ( 6) },
580 { "g", tZONE, HOUR ( 7) },
581 { "h", tZONE, HOUR ( 8) },
582 { "i", tZONE, HOUR ( 9) },
583 { "k", tZONE, HOUR ( 10) },
584 { "l", tZONE, HOUR ( 11) },
585 { "m", tZONE, HOUR ( 12) },
586 { "n", tZONE, HOUR (- 1) },
587 { "o", tZONE, HOUR (- 2) },
588 { "p", tZONE, HOUR (- 3) },
589 { "q", tZONE, HOUR (- 4) },
590 { "r", tZONE, HOUR (- 5) },
591 { "s", tZONE, HOUR (- 6) },
592 { "t", tZONE, HOUR (- 7) },
593 { "u", tZONE, HOUR (- 8) },
594 { "v", tZONE, HOUR (- 9) },
595 { "w", tZONE, HOUR (-10) },
596 { "x", tZONE, HOUR (-11) },
597 { "y", tZONE, HOUR (-12) },
598 { "z", tZONE, HOUR ( 0) },
614 ToHour (Hours, Meridian)
621 if (Hours < 0 || Hours > 23)
625 if (Hours < 1 || Hours > 12)
631 if (Hours < 1 || Hours > 12)
649 /* XPG4 suggests that years 00-68 map to 2000-2068, and
650 years 69-99 map to 1969-1999. */
665 register const TABLE *tp;
669 /* Make it lowercase. */
670 for (p = buff; *p; p++)
674 if (strcmp (buff, "am") == 0 || strcmp (buff, "a.m.") == 0)
676 yylval.Meridian = MERam;
679 if (strcmp (buff, "pm") == 0 || strcmp (buff, "p.m.") == 0)
681 yylval.Meridian = MERpm;
685 /* See if we have an abbreviation for a month. */
686 if (strlen (buff) == 3)
688 else if (strlen (buff) == 4 && buff[3] == '.')
696 for (tp = MonthDayTable; tp->name; tp++)
700 if (strncmp (buff, tp->name, 3) == 0)
702 yylval.Number = tp->value;
706 else if (strcmp (buff, tp->name) == 0)
708 yylval.Number = tp->value;
713 for (tp = TimezoneTable; tp->name; tp++)
714 if (strcmp (buff, tp->name) == 0)
716 yylval.Number = tp->value;
720 if (strcmp (buff, "dst") == 0)
723 for (tp = UnitsTable; tp->name; tp++)
724 if (strcmp (buff, tp->name) == 0)
726 yylval.Number = tp->value;
730 /* Strip off any plural and try the units table again. */
731 i = strlen (buff) - 1;
735 for (tp = UnitsTable; tp->name; tp++)
736 if (strcmp (buff, tp->name) == 0)
738 yylval.Number = tp->value;
741 buff[i] = 's'; /* Put back for "this" in OtherTable. */
744 for (tp = OtherTable; tp->name; tp++)
745 if (strcmp (buff, tp->name) == 0)
747 yylval.Number = tp->value;
751 /* Military timezones. */
752 if (buff[1] == '\0' && ISALPHA (*buff))
754 for (tp = MilitaryTable; tp->name; tp++)
755 if (strcmp (buff, tp->name) == 0)
757 yylval.Number = tp->value;
762 /* Drop out any periods and try the timezone table again. */
763 for (i = 0, p = q = buff; *q; q++)
770 for (tp = TimezoneTable; tp->name; tp++)
771 if (strcmp (buff, tp->name) == 0)
773 yylval.Number = tp->value;
791 while (ISSPACE (*yyInput))
794 if (ISDIGIT (c = *yyInput) || c == '-' || c == '+')
796 if (c == '-' || c == '+')
798 sign = c == '-' ? -1 : 1;
799 if (!ISDIGIT (*++yyInput))
800 /* skip the '-' sign */
805 for (yylval.Number = 0; ISDIGIT (c = *yyInput++);)
806 yylval.Number = 10 * yylval.Number + c - '0';
809 yylval.Number = -yylval.Number;
810 return sign ? tSNUMBER : tUNUMBER;
814 for (p = buff; (c = *yyInput++, ISALPHA (c)) || c == '.';)
815 if (p < &buff[sizeof buff - 1])
819 return LookupWord (buff);
838 #define TM_YEAR_ORIGIN 1900
840 #ifndef GETDATE_IGNORE_TIMEZONE
842 /* Yield A - B, measured in seconds. */
847 int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
848 int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
850 /* difference in day of year */
851 a->tm_yday - b->tm_yday
852 /* + intervening leap days */
853 + ((ay >> 2) - (by >> 2))
854 - (ay / 100 - by / 100)
855 + ((ay / 100 >> 2) - (by / 100 >> 2))
856 /* + difference in years * 365 */
857 + (long) (ay - by) * 365
859 return (60 * (60 * (24 * days + (a->tm_hour - b->tm_hour))
860 + (a->tm_min - b->tm_min))
861 + (a->tm_sec - b->tm_sec));
871 struct tm tm, tm0, *tmp;
875 Start = now ? *now : time ((time_t *) NULL);
876 tmp = localtime (&Start);
877 yyYear = tmp->tm_year + TM_YEAR_ORIGIN;
878 yyMonth = tmp->tm_mon + 1;
879 yyDay = tmp->tm_mday;
880 yyHour = tmp->tm_hour;
881 yyMinutes = tmp->tm_min;
882 yySeconds = tmp->tm_sec;
897 || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
900 tm.tm_year = ToYear (yyYear) - TM_YEAR_ORIGIN + yyRelYear;
901 tm.tm_mon = yyMonth - 1 + yyRelMonth;
902 tm.tm_mday = yyDay + yyRelDay;
903 if (yyHaveTime || (yyHaveRel && !yyHaveDate && !yyHaveDay))
905 tm.tm_hour = ToHour (yyHour, yyMeridian);
908 tm.tm_min = yyMinutes;
909 tm.tm_sec = yySeconds;
913 tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
915 tm.tm_hour += yyRelHour;
916 tm.tm_min += yyRelMinutes;
917 tm.tm_sec += yyRelSeconds;
921 Start = mktime (&tm);
923 if (Start == (time_t) -1)
926 /* Guard against falsely reporting errors near the time_t boundaries
927 when parsing times in other time zones. For example, if the min
928 time_t value is 1970-01-01 00:00:00 UTC and we are 8 hours ahead
929 of UTC, then the min localtime value is 1970-01-01 08:00:00; if
930 we apply mktime to 1970-01-01 00:00:00 we will get an error, so
931 we apply mktime to 1970-01-02 08:00:00 instead and adjust the time
932 zone by 24 hours to compensate. This algorithm assumes that
933 there is no DST transition within a day of the time_t boundaries. */
935 #ifndef GETDATE_IGNORE_TIMEZONE
939 if (tm.tm_year <= EPOCH - TM_YEAR_ORIGIN)
942 yyTimezone -= 24 * 60;
947 yyTimezone += 24 * 60;
949 Start = mktime (&tm);
953 if (Start == (time_t) -1)
957 if (yyHaveDay && !yyHaveDate)
959 tm.tm_mday += ((yyDayNumber - tm.tm_wday + 7) % 7
960 + 7 * (yyDayOrdinal - (0 < yyDayOrdinal)));
961 Start = mktime (&tm);
962 if (Start == (time_t) -1)
966 #ifndef GETDATE_IGNORE_TIMEZONE
969 long delta = yyTimezone * 60L + difftm (&tm, gmtime (&Start));
970 if ((Start + delta < Start) != (delta < 0))
971 return -1; /* time_t overflow */
987 char buff[MAX_BUFF_LEN + 1];
990 (void) printf ("Enter date, or blank line to exit.\n\t> ");
991 (void) fflush (stdout);
993 buff[MAX_BUFF_LEN] = 0;
994 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
996 d = get_date (buff, (time_t *) NULL);
998 (void) printf ("Bad format - couldn't convert.\n");
1000 (void) printf ("%s", ctime (&d));
1001 (void) printf ("\t> ");
1002 (void) fflush (stdout);
1007 #endif /* defined (TEST) */