[BACK]Return to engine.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Annotation of src/usr.bin/make/engine.c, Revision 1.3

1.3     ! espie       1: /*     $OpenBSD: engine.c,v 1.2 2007/09/16 12:09:36 espie Exp $ */
1.1       espie       2: /*
                      3:  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
                      4:  * Copyright (c) 1988, 1989 by Adam de Boor
                      5:  * Copyright (c) 1989 by Berkeley Softworks
                      6:  * All rights reserved.
                      7:  *
                      8:  * This code is derived from software contributed to Berkeley by
                      9:  * Adam de Boor.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. Neither the name of the University nor the names of its contributors
                     20:  *    may be used to endorse or promote products derived from this software
                     21:  *    without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     33:  * SUCH DAMAGE.
                     34:  */
                     35:
                     36: #include <limits.h>
                     37: #include <stdio.h>
                     38: #include <errno.h>
                     39: #include <fcntl.h>
                     40: #include <unistd.h>
                     41: #include <string.h>
                     42: #include "config.h"
                     43: #include "defines.h"
                     44: #include "dir.h"
                     45: #include "engine.h"
                     46: #include "arch.h"
                     47: #include "gnode.h"
                     48: #include "targ.h"
                     49: #include "var.h"
                     50: #include "extern.h"
                     51: #include "lst.h"
                     52: #include "timestamp.h"
                     53: #include "make.h"
                     54: #include "main.h"
                     55:
                     56: static void MakeTimeStamp(void *, void *);
                     57: static void MakeAddAllSrc(void *, void *);
                     58:
                     59: /*-
                     60:  *-----------------------------------------------------------------------
                     61:  * Job_CheckCommands --
                     62:  *     Make sure the given node has all the commands it needs.
                     63:  *
                     64:  * Results:
                     65:  *     true if the commands list is/was ok.
                     66:  *
                     67:  * Side Effects:
                     68:  *     The node will have commands from the .DEFAULT rule added to it
                     69:  *     if it needs them.
                     70:  *-----------------------------------------------------------------------
                     71:  */
                     72: bool
1.3     ! espie      73: Job_CheckCommands(GNode *gn, void (*abortProc)(char *, ...))
1.1       espie      74: {
1.3     ! espie      75:        if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->commands) &&
        !            76:            (gn->type & OP_LIB) == 0) {
        !            77:                /*
        !            78:                 * No commands. Look for .DEFAULT rule from which we might infer
        !            79:                 * commands
        !            80:                 */
        !            81:                if (DEFAULT != NULL && !Lst_IsEmpty(&DEFAULT->commands)) {
        !            82:                        /*
        !            83:                         * Make only looks for a .DEFAULT if the node was never
        !            84:                         * the target of an operator, so that's what we do too.
        !            85:                         * If a .DEFAULT was given, we substitute its commands
        !            86:                         * for gn's commands and set the IMPSRC variable to be
        !            87:                         * the target's name The DEFAULT node acts like a
        !            88:                         * transformation rule, in that gn also inherits any
        !            89:                         * attributes or sources attached to .DEFAULT itself.
        !            90:                         */
        !            91:                        Make_HandleUse(DEFAULT, gn);
        !            92:                        Varq_Set(IMPSRC_INDEX, Varq_Value(TARGET_INDEX, gn), gn);
        !            93:                } else if (is_out_of_date(Dir_MTime(gn))) {
        !            94:                        /*
        !            95:                         * The node wasn't the target of an operator we have no
        !            96:                         * .DEFAULT rule to go on and the target doesn't
        !            97:                         * already exist. There's nothing more we can do for
        !            98:                         * this branch. If the -k flag wasn't given, we stop in
        !            99:                         * our tracks, otherwise we just don't update this
        !           100:                         * node's parents so they never get examined.
        !           101:                         */
        !           102:                        static const char msg[] =
        !           103:                            "make: don't know how to make";
        !           104:
        !           105:                        if (gn->type & OP_OPTIONAL) {
        !           106:                                (void)fprintf(stdout, "%s %s(ignored)\n", msg,
        !           107:                                    gn->name);
        !           108:                                (void)fflush(stdout);
        !           109:                        } else if (keepgoing) {
        !           110:                                (void)fprintf(stdout, "%s %s(continuing)\n",
        !           111:                                    msg, gn->name);
        !           112:                                (void)fflush(stdout);
        !           113:                                return false;
        !           114:                        } else {
        !           115:                                (*abortProc)("%s %s. Stop in %s.", msg,
        !           116:                                    gn->name, Var_Value(".CURDIR"));
        !           117:                                return false;
        !           118:                        }
        !           119:                }
1.1       espie     120:        }
1.3     ! espie     121:        return true;
1.1       espie     122: }
                    123:
                    124: /*-
                    125:  *-----------------------------------------------------------------------
                    126:  * Job_Touch --
                    127:  *     Touch the given target. Called by JobStart when the -t flag was
                    128:  *     given
                    129:  *
                    130:  * Side Effects:
                    131:  *     The data modification of the file is changed. In addition, if the
                    132:  *     file did not exist, it is created.
                    133:  *-----------------------------------------------------------------------
                    134:  */
                    135: void
1.3     ! espie     136: Job_Touch(GNode *gn, bool silent)
1.1       espie     137: {
1.3     ! espie     138:        int streamID;   /* ID of stream opened to do the touch */
1.1       espie     139:
1.3     ! espie     140:        if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) {
        !           141:                /*
        !           142:                 * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual"
        !           143:                 * targets and, as such, shouldn't really be created.
        !           144:                 */
        !           145:                return;
        !           146:        }
1.1       espie     147:
1.3     ! espie     148:        if (!silent) {
        !           149:                (void)fprintf(stdout, "touch %s\n", gn->name);
        !           150:                (void)fflush(stdout);
        !           151:        }
1.1       espie     152:
1.3     ! espie     153:        if (noExecute) {
        !           154:                return;
        !           155:        }
1.1       espie     156:
1.3     ! espie     157:        if (gn->type & OP_ARCHV) {
        !           158:                Arch_Touch(gn);
        !           159:        } else if (gn->type & OP_LIB) {
        !           160:                Arch_TouchLib(gn);
        !           161:        } else {
        !           162:                const char *file = gn->path != NULL ? gn->path : gn->name;
        !           163:
        !           164:                if (set_times(file) == -1){
        !           165:                        streamID = open(file, O_RDWR | O_CREAT, 0666);
        !           166:
        !           167:                        if (streamID >= 0) {
        !           168:                                char    c;
        !           169:
        !           170:                                /*
        !           171:                                 * Read and write a byte to the file to change
        !           172:                                 * the modification time, then close the file.
        !           173:                                 */
        !           174:                                if (read(streamID, &c, 1) == 1) {
        !           175:                                        (void)lseek(streamID, 0, SEEK_SET);
        !           176:                                        (void)write(streamID, &c, 1);
        !           177:                                }
        !           178:
        !           179:                                (void)close(streamID);
        !           180:                        } else {
        !           181:                                (void)fprintf(stdout,
        !           182:                                    "*** couldn't touch %s: %s", file,
        !           183:                                    strerror(errno));
        !           184:                                (void)fflush(stdout);
        !           185:                        }
1.1       espie     186:                }
                    187:        }
                    188: }
                    189:
                    190: /*-
                    191:  *-----------------------------------------------------------------------
                    192:  * Make_TimeStamp --
                    193:  *     Set the cmtime field of a parent node based on the mtime stamp in its
                    194:  *     child.
                    195:  *
                    196:  * Side Effects:
                    197:  *     The cmtime of the parent node will be changed if the mtime
                    198:  *     field of the child is greater than it.
                    199:  *-----------------------------------------------------------------------
                    200:  */
                    201: void
                    202: Make_TimeStamp(
                    203:     GNode *pgn, /* the current parent */
                    204:     GNode *cgn) /* the child we've just examined */
                    205: {
1.3     ! espie     206:        if (is_strictly_before(pgn->cmtime, cgn->mtime))
        !           207:                pgn->cmtime = cgn->mtime;
1.1       espie     208: }
                    209:
                    210: /*-
                    211:  *-----------------------------------------------------------------------
                    212:  * Make_HandleUse --
                    213:  *     Function called by Make_Run and SuffApplyTransform on the downward
                    214:  *     pass to handle .USE and transformation nodes. A callback function
                    215:  *     for Lst_ForEach, it implements the .USE and transformation
                    216:  *     functionality by copying the node's commands, type flags
                    217:  *     and children to the parent node. Should be called before the
                    218:  *     children are enqueued to be looked at by MakeAddChild.
                    219:  *
                    220:  *     A .USE node is much like an explicit transformation rule, except
                    221:  *     its commands are always added to the target node, even if the
                    222:  *     target already has commands.
                    223:  *
                    224:  * Side Effects:
                    225:  *     Children and commands may be added to the parent and the parent's
                    226:  *     type may be changed.
                    227:  *
                    228:  *-----------------------------------------------------------------------
                    229:  */
                    230: void
                    231: Make_HandleUse(
                    232:     GNode      *cgn,   /* The .USE node */
                    233:     GNode      *pgn)   /* The target of the .USE node */
                    234: {
1.3     ! espie     235:        GNode   *gn;    /* A child of the .USE node */
        !           236:        LstNode ln;     /* An element in the children list */
1.1       espie     237:
1.3     ! espie     238:        if (cgn->type & (OP_USE|OP_TRANSFORM)) {
        !           239:                if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) {
        !           240:                        /* .USE or transformation and target has no commands --
        !           241:                         * append the child's commands to the parent.  */
        !           242:                        Lst_Concat(&pgn->commands, &cgn->commands);
        !           243:                }
1.1       espie     244:
1.3     ! espie     245:                for (ln = Lst_First(&cgn->children); ln != NULL;
        !           246:                    ln = Lst_Adv(ln)) {
        !           247:                        gn = (GNode *)Lst_Datum(ln);
        !           248:
        !           249:                        if (Lst_AddNew(&pgn->children, gn)) {
        !           250:                                Lst_AtEnd(&gn->parents, pgn);
        !           251:                                pgn->unmade += 1;
        !           252:                        }
        !           253:                }
1.1       espie     254:
1.3     ! espie     255:                pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM);
1.1       espie     256:
1.3     ! espie     257:                /*
        !           258:                 * This child node is now "made", so we decrement the count of
        !           259:                 * unmade children in the parent... We also remove the child
        !           260:                 * from the parent's list to accurately reflect the number of
        !           261:                 * decent children the parent has. This is used by Make_Run to
        !           262:                 * decide whether to queue the parent or examine its
        !           263:                 * children...
        !           264:                 */
        !           265:                if (cgn->type & OP_USE) {
        !           266:                        pgn->unmade--;
        !           267:                }
1.1       espie     268:        }
                    269: }
                    270:
                    271: /*-
                    272:  *-----------------------------------------------------------------------
                    273:  * MakeAddAllSrc --
                    274:  *     Add a child's name to the ALLSRC and OODATE variables of the given
                    275:  *     node. Called from Make_DoAllVar via Lst_ForEach. A child is added only
                    276:  *     if it has not been given the .EXEC, .USE or .INVISIBLE attributes.
                    277:  *     .EXEC and .USE children are very rarely going to be files, so...
                    278:  *     A child is added to the OODATE variable if its modification time is
                    279:  *     later than that of its parent, as defined by Make, except if the
                    280:  *     parent is a .JOIN node. In that case, it is only added to the OODATE
                    281:  *     variable if it was actually made (since .JOIN nodes don't have
                    282:  *     modification times, the comparison is rather unfair...)..
                    283:  *
                    284:  * Side Effects:
                    285:  *     The ALLSRC variable for the given node is extended.
                    286:  *-----------------------------------------------------------------------
                    287:  */
                    288: static void
                    289: MakeAddAllSrc(
                    290:     void *cgnp, /* The child to add */
                    291:     void *pgnp) /* The parent to whose ALLSRC variable it should be */
                    292:                        /* added */
                    293: {
1.3     ! espie     294:        GNode *cgn = (GNode *)cgnp;
        !           295:        GNode *pgn = (GNode *)pgnp;
        !           296:        if ((cgn->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) == 0) {
        !           297:                const char *child;
        !           298:
        !           299:                if (OP_NOP(cgn->type) ||
        !           300:                    (child = Varq_Value(TARGET_INDEX, cgn)) == NULL) {
        !           301:                        /*
        !           302:                         * this node is only source; use the specific pathname
        !           303:                         * for it
        !           304:                         */
        !           305:                        child = cgn->path != NULL ? cgn->path : cgn->name;
        !           306:                }
1.1       espie     307:
1.3     ! espie     308:                Varq_Append(ALLSRC_INDEX, child, pgn);
        !           309:                if (pgn->type & OP_JOIN) {
        !           310:                        if (cgn->made == MADE) {
        !           311:                                Varq_Append(OODATE_INDEX, child, pgn);
        !           312:                        }
        !           313:                } else if (is_strictly_before(pgn->mtime, cgn->mtime) ||
        !           314:                    (!is_strictly_before(cgn->mtime, now) &&
        !           315:                    cgn->made == MADE)) {
        !           316:                        /*
        !           317:                         * It goes in the OODATE variable if the parent is
        !           318:                         * younger than the child or if the child has been
        !           319:                         * modified more recently than the start of the make.
        !           320:                         * This is to keep pmake from getting confused if
        !           321:                         * something else updates the parent after the make
        !           322:                         * starts (shouldn't happen, I know, but sometimes it
        !           323:                         * does). In such a case, if we've updated the kid, the
        !           324:                         * parent is likely to have a modification time later
        !           325:                         * than that of the kid and anything that relies on the
        !           326:                         * OODATE variable will be hosed.
        !           327:                         *
        !           328:                         */
        !           329:                        Varq_Append(OODATE_INDEX, child, pgn);
        !           330:                }
1.1       espie     331:        }
                    332: }
                    333:
                    334: /*-
                    335:  *-----------------------------------------------------------------------
                    336:  * Make_DoAllVar --
                    337:  *     Set up the ALLSRC and OODATE variables. Sad to say, it must be
                    338:  *     done separately, rather than while traversing the graph. This is
                    339:  *     because Make defined OODATE to contain all sources whose modification
                    340:  *     times were later than that of the target, *not* those sources that
                    341:  *     were out-of-date. Since in both compatibility and native modes,
                    342:  *     the modification time of the parent isn't found until the child
                    343:  *     has been dealt with, we have to wait until now to fill in the
                    344:  *     variable. As for ALLSRC, the ordering is important and not
                    345:  *     guaranteed when in native mode, so it must be set here, too.
                    346:  *
                    347:  * Side Effects:
                    348:  *     The ALLSRC and OODATE variables of the given node is filled in.
                    349:  *     If the node is a .JOIN node, its TARGET variable will be set to
                    350:  *     match its ALLSRC variable.
                    351:  *-----------------------------------------------------------------------
                    352:  */
                    353: void
                    354: Make_DoAllVar(GNode *gn)
                    355: {
1.3     ! espie     356:        Lst_ForEach(&gn->children, MakeAddAllSrc, gn);
1.1       espie     357:
1.3     ! espie     358:        if (Varq_Value(OODATE_INDEX, gn) == NULL)
        !           359:                Varq_Set(OODATE_INDEX, "", gn);
        !           360:        if (Varq_Value(ALLSRC_INDEX, gn) == NULL)
        !           361:                Varq_Set(ALLSRC_INDEX, "", gn);
1.1       espie     362:
1.3     ! espie     363:        if (gn->type & OP_JOIN)
        !           364:                Varq_Set(TARGET_INDEX, Varq_Value(ALLSRC_INDEX, gn), gn);
1.1       espie     365: }
                    366:
                    367: /* Wrapper to call Make_TimeStamp from a forEach loop. */
                    368: static void
                    369: MakeTimeStamp(
                    370:     void *pgn, /* the current parent */
                    371:     void *cgn) /* the child we've just examined */
                    372: {
1.3     ! espie     373:        Make_TimeStamp((GNode *)pgn, (GNode *)cgn);
1.1       espie     374: }
                    375:
                    376: /*-
                    377:  *-----------------------------------------------------------------------
                    378:  * Make_OODate --
                    379:  *     See if a given node is out of date with respect to its sources.
                    380:  *     Used by Make_Run when deciding which nodes to place on the
                    381:  *     toBeMade queue initially and by Make_Update to screen out USE and
                    382:  *     EXEC nodes. In the latter case, however, any other sort of node
                    383:  *     must be considered out-of-date since at least one of its children
                    384:  *     will have been recreated.
                    385:  *
                    386:  * Results:
                    387:  *     true if the node is out of date. false otherwise.
                    388:  *
                    389:  * Side Effects:
                    390:  *     The mtime field of the node and the cmtime field of its parents
                    391:  *     will/may be changed.
                    392:  *-----------------------------------------------------------------------
                    393:  */
                    394: bool
                    395: Make_OODate(GNode *gn) /* the node to check */
                    396: {
1.3     ! espie     397:        bool oodate;
1.1       espie     398:
                    399:        /*
1.3     ! espie     400:         * Certain types of targets needn't even be sought as their datedness
        !           401:         * doesn't depend on their modification time...
1.1       espie     402:         */
1.3     ! espie     403:        if ((gn->type & (OP_JOIN|OP_USE|OP_EXEC)) == 0) {
        !           404:                (void)Dir_MTime(gn);
        !           405:                if (DEBUG(MAKE)) {
        !           406:                        if (!is_out_of_date(gn->mtime)) {
        !           407:                                printf("modified %s...",
        !           408:                                    time_to_string(gn->mtime));
        !           409:                        } else {
        !           410:                                printf("non-existent...");
        !           411:                        }
        !           412:                }
1.1       espie     413:        }
                    414:
                    415:        /*
1.3     ! espie     416:         * A target is remade in one of the following circumstances:
        !           417:         *      its modification time is smaller than that of its youngest child
        !           418:         *          and it would actually be run (has commands or type OP_NOP)
        !           419:         *      it's the object of a force operator
        !           420:         *      it has no children, was on the lhs of an operator and doesn't
        !           421:         *      exist already.
        !           422:         *
        !           423:         * Libraries are only considered out-of-date if the archive module says
        !           424:         * they are.
        !           425:         *
        !           426:         * These weird rules are brought to you by Backward-Compatibility and
        !           427:         * the strange people who wrote 'Make'.
1.1       espie     428:         */
1.3     ! espie     429:        if (gn->type & OP_USE) {
        !           430:                /*
        !           431:                 * If the node is a USE node it is *never* out of date
        !           432:                 * no matter *what*.
        !           433:                 */
        !           434:                if (DEBUG(MAKE)) {
        !           435:                        printf(".USE node...");
        !           436:                }
        !           437:                oodate = false;
        !           438:        } else if ((gn->type & OP_LIB) && Arch_IsLib(gn)) {
        !           439:                if (DEBUG(MAKE)) {
        !           440:                        printf("library...");
        !           441:                }
1.1       espie     442:
1.3     ! espie     443:                /*
        !           444:                 * always out of date if no children and :: target
        !           445:                 */
        !           446:
        !           447:                oodate = Arch_LibOODate(gn) ||
        !           448:                    (is_out_of_date(gn->cmtime) && (gn->type & OP_DOUBLEDEP));
        !           449:        } else if (gn->type & OP_JOIN) {
        !           450:                /*
        !           451:                 * A target with the .JOIN attribute is only considered
        !           452:                 * out-of-date if any of its children was out-of-date.
        !           453:                 */
        !           454:                if (DEBUG(MAKE)) {
        !           455:                        printf(".JOIN node...");
        !           456:                }
        !           457:                oodate = gn->childMade;
        !           458:        } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) {
        !           459:                /*
        !           460:                 * A node which is the object of the force (!) operator or
        !           461:                 * which has the .EXEC attribute is always considered
        !           462:                 * out-of-date.
        !           463:                 */
        !           464:                if (DEBUG(MAKE)) {
        !           465:                        if (gn->type & OP_FORCE) {
        !           466:                                printf("! operator...");
        !           467:                        } else if (gn->type & OP_PHONY) {
        !           468:                                printf(".PHONY node...");
        !           469:                        } else {
        !           470:                                printf(".EXEC node...");
        !           471:                        }
        !           472:                }
        !           473:                oodate = true;
        !           474:        } else if (is_strictly_before(gn->mtime, gn->cmtime) ||
        !           475:            (is_out_of_date(gn->cmtime) &&
        !           476:            (is_out_of_date(gn->mtime) || (gn->type & OP_DOUBLEDEP)))) {
        !           477:                /*
        !           478:                 * A node whose modification time is less than that of its
        !           479:                 * youngest child or that has no children (cmtime ==
        !           480:                 * OUT_OF_DATE) and either doesn't exist (mtime == OUT_OF_DATE)
        !           481:                 * or was the object of a :: operator is out-of-date. Why?
        !           482:                 * Because that's the way Make does it.
        !           483:                 */
        !           484:                if (DEBUG(MAKE)) {
        !           485:                        if (is_strictly_before(gn->mtime, gn->cmtime)) {
        !           486:                                printf("modified before source...");
        !           487:                        } else if (is_out_of_date(gn->mtime)) {
        !           488:                                printf("non-existent and no sources...");
        !           489:                        } else {
        !           490:                                printf(":: operator and no sources...");
        !           491:                        }
        !           492:                }
        !           493:                oodate = true;
        !           494:        } else {
1.1       espie     495: #if 0
1.3     ! espie     496:                /* WHY? */
        !           497:                if (DEBUG(MAKE)) {
        !           498:                        printf("source %smade...", gn->childMade ? "" : "not ");
        !           499:                }
        !           500:                oodate = gn->childMade;
1.1       espie     501: #else
1.3     ! espie     502:                oodate = false;
1.1       espie     503: #endif /* 0 */
1.3     ! espie     504:        }
1.1       espie     505:
1.3     ! espie     506:        /*
        !           507:         * If the target isn't out-of-date, the parents need to know its
        !           508:         * modification time. Note that targets that appear to be out-of-date
        !           509:         * but aren't, because they have no commands and aren't of type OP_NOP,
        !           510:         * have their mtime stay below their children's mtime to keep parents
        !           511:         * from thinking they're out-of-date.
        !           512:         */
        !           513:        if (!oodate)
        !           514:                Lst_ForEach(&gn->parents, MakeTimeStamp, gn);
1.1       espie     515:
1.3     ! espie     516:        return oodate;
1.1       espie     517: }
                    518: