static int DaoParser_MacroApply( DaoParser *self, DArray *tokens, DMacroGroup *group, DMap *tokMap, DMap *used, int level, DString *tag, int pos0, int adjust ) { DMacroUnit **units = (DMacroUnit**) group->units->items.pVoid; DMacroUnit *unit; DMacroGroup *grp; DMacroNode *node, *node2; DArray *toks = DArray_New(D_TOKEN); DaoToken *tk = DaoToken_New(); DaoToken *tt = NULL; DNode *kwnode = NULL; DMap *check = NULL; DMap one = { NULL, 0, 0, 0 }; int M, N = group->units->size; int i, j, gid = -1; int repeated = 0; int start_mbs = -1; int start_wcs = -1; int squote, dquote; if( group->repeat != DMACRO_AUTO ) level ++; for( i=0; i<N; i++ ){ unit = units[i]; if( tokens->size >0 ) pos0 = tokens->items.pToken[ tokens->size -1 ]->line; self->curLine = pos0; /* printf( "apply unit %i: %i\n", i, unit->type ); */ switch( unit->type ){ case DMACRO_TOK : squote = unit->marker->type == DTOK_ESC_SQUO; dquote = unit->marker->type == DTOK_ESC_DQUO; if( (squote && start_mbs >=0) || (dquote && start_wcs >=0) ){ int qstart = squote ? start_mbs : start_wcs; tt = tokens->items.pToken[ qstart ]; for(j=qstart+1,M=tokens->size; j<M; j++){ DaoToken *jtok = tokens->items.pToken[j]; int t = j ? tokens->items.pToken[j-1]->type : 0; if( t == DTOK_IDENTIFIER && jtok->type == t ) DString_AppendChar( & tt->string, ' ' ); DString_Append( & tt->string, & jtok->string ); } if( squote ){ DString_AppendChar( & tt->string, '\'' ); DArray_Erase( tokens, start_mbs+1, tokens->size ); }else{ DString_AppendChar( & tt->string, '\"' ); DArray_Erase( tokens, start_wcs+1, tokens->size ); } start_mbs = -1; break; }else if( squote ){ start_mbs = tokens->size; DArray_Append( tokens, unit->marker ); tt = tokens->items.pToken[ start_mbs ]; tt->type = tt->name = DTOK_MBS; DString_SetMBS( & tt->string, "\'" ); break; }else if( dquote ){ start_wcs = tokens->size; DArray_Append( tokens, unit->marker ); tt = tokens->items.pToken[ start_wcs ]; tt->type = tt->name = DTOK_WCS; DString_SetMBS( & tt->string, "\"" ); break; } DArray_Append( tokens, unit->marker ); tokens->items.pToken[ tokens->size-1 ]->cpos += adjust; break; case DMACRO_VAR : DaoToken_Assign( tk, unit->marker ); DString_Append( & tk->string, tag ); DArray_Append( tokens, tk ); break; case DMACRO_EXP : case DMACRO_ID : case DMACRO_OP : case DMACRO_BL : case DMACRO_IBL : kwnode = MAP_Find( tokMap, & unit->marker->string ); if( kwnode ==NULL ){ DaoParser_Error( self, DAO_CTW_UNDEF_MAC_MARKER, & unit->marker->string ); goto Failed; } node = (DMacroNode*) kwnode->value.pVoid; kwnode = MAP_Find( used, unit ); if( kwnode == NULL ){ DMap_Insert( used, unit, & one ); kwnode = MAP_Find( used, unit ); } check = (DMap*) kwnode->value.pVoid; repeated = 1; /* printf( ">>>\n%s level %i: \n", unit->marker->string.mbs, level ); DMacroNode_Print( node ); printf( "\n" ); */ /* search a leaf */ node2 = DMacroNode_FindLeaf( node, check, level ); if( node2 ){ /* printf( "appending tokens\n" ); DMacroNode_Print( node2 ); printf( "\n" ); */ DArray_InsertArray( tokens, tokens->size, node2->leaves, 0, -1 ); DMap_Insert( check, node2, NULL ); /* DArray_Clear( node2->leaves ); */ }else{ DMacroNode_RemoveEmptyLeftBranch( node, level ); goto Failed; } break; case DMACRO_GRP : case DMACRO_ALT : grp = (DMacroGroup*) unit; DArray_Clear( toks ); j = DaoParser_MacroApply( self, toks, grp, tokMap, used, level, tag, pos0, adjust ); switch( grp->repeat ){ case DMACRO_AUTO : case DMACRO_ONE : if( j <0 && group->type != DMACRO_ALT ) goto Failed; repeated = (j>0); if( j >=0 ){ gid = i; DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } break; case DMACRO_ZERO_OR_ONE : gid = i; repeated = (j>0); if( j >=0 ){ DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } break; case DMACRO_ZERO_OR_MORE : gid = i; repeated = (j>0); if( j >=0 ){ DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } while( j >0 ){ DArray_Clear( toks ); j = DaoParser_MacroApply( self, toks, grp, tokMap, used, level, tag, pos0, adjust ); if( j >0 ){ DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } } break; case DMACRO_ONE_OR_MORE : if( j <0 && group->type != DMACRO_ALT ) goto Failed; repeated = (j>0); if( j >=0 ){ DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } while( j >0 ){ gid = i; DArray_Clear( toks ); j = DaoParser_MacroApply( self, toks, grp, tokMap, used, level, tag, pos0, adjust ); if( j >0 ){ DArray_InsertArray( tokens, tokens->size, toks, 0, -1 ); } } break; } break; default : goto Failed; } if( group->type == DMACRO_ALT && gid >=0 ) break; } if( group->repeat != DMACRO_AUTO ) level --; if( group->type == DMACRO_ALT && gid <0 ) goto Failed; DaoToken_Delete( tk ); DArray_Delete( toks ); return repeated; Failed : DaoToken_Delete( tk ); DArray_Delete( toks ); return -1; }
static void SYS_Ctimef( DaoProcess *proc, DaoValue *p[], int N ) { int i; int halfday = 0; const int size = p[1]->xString.data->size; const char *format = DString_GetMBS( p[1]->xString.data ); char buf[100]; char *p1 = buf+1; char *p2; DaoMap *sym = NULL; DaoString *ds = DaoString_New(1); DaoValue *key = (DaoValue*) ds; DString *S; struct tm *ctime; time_t t = (time_t)p[0]->xInteger.value; if( t == 0 ) t = time(NULL); ctime = gmtime( & t ); if( N > 1 ){ sym = (DaoMap*)p[2]; if( sym->items->size == 0 ) sym = NULL; } S = DaoProcess_PutMBString( proc, "" ); for( i=0; i+1<size; i++ ){ if( format[i] == '%' && ( format[i+1] == 'a' || format[i+1] == 'A' ) ){ halfday = 1; break; } } buf[0] = '0'; /* for padding */ for( i=0; i+1<size; i++ ){ p2 = p1; p1[0] = 0; if( format[i] == '%' ){ const char ch = format[i+1]; switch( ch ){ case 'Y' : sprintf( p1, "%i", ctime->tm_year+1900 ); break; case 'y' : sprintf( p1, "%i", ctime->tm_year+1900 ); p2 += 2; break; case 'M' : case 'm' : if( ! addStringFromMap( key, S, sym, "month", ctime->tm_mon ) ){ sprintf( p1, "%i", ctime->tm_mon+1 ); if( ch=='M' && p1[1]==0 ) p2 = buf; /* padding 0; */ }else p2 = NULL; break; case 'D' : case 'd' : if( ! addStringFromMap( key, S, sym, "date", ctime->tm_mday ) ){ sprintf( p1, "%i", ctime->tm_mday ); if( ch=='D' && p1[1]==0 ) p2 = buf; /* padding 0; */ }else p2 = NULL; break; case 'W' : case 'w' : if( ! addStringFromMap( key, S, sym, "week", ctime->tm_wday ) ) sprintf( p1, "%i", ctime->tm_wday+1 ); else p2 = NULL; break; case 'H' : case 'h' : if( halfday ) sprintf( p1, "%i", ctime->tm_hour %12 ); else sprintf( p1, "%i", ctime->tm_hour ); if( ch=='H' && p1[1]==0 ) p2 = buf; /* padding 0; */ break; case 'I' : case 'i' : sprintf( p1, "%i", ctime->tm_min ); if( ch=='I' && p1[1]==0 ) p2 = buf; /* padding 0; */ break; case 'S' : case 's' : sprintf( p1, "%i", ctime->tm_sec ); if( ch=='S' && p1[1]==0 ) p2 = buf; /* padding 0; */ break; case 'a' : if( ! addStringFromMap( key, S, sym, "halfday", 0 ) ){ if( ctime->tm_hour >= 12 ) strcpy( p1, "pm" ); else strcpy( p1, "am" ); }else p2 = NULL; break; case 'A' : if( ! addStringFromMap( key, S, sym, "halfday", 1 ) ){ if( ctime->tm_hour >= 12 ) strcpy( p1, "PM" ); else strcpy( p1, "AM" ); }else p2 = NULL; break; default : break; } if( p2 ) DString_AppendMBS( S, p2 ); i ++; }else{ DString_AppendChar( S, format[i] ); } } if( i+1 == size ) DString_AppendChar( S, format[i] ); DaoString_Delete( ds ); }
int main( int argc, char **argv ) { int restart = 0; int i, k, idsrc, vmods = 0; DString *opts, *args; /*mtrace(); */ for(i=1; i<argc; i++){ if( strcmp( argv[i], "-r" ) ==0 || strcmp( argv[i], "--restart" ) ==0 ){ restart = i; break; } } if( restart ) DaoRestartRun( argv, argc, restart ); vmSpace = DaoInit( argv[0] ); idsrc = -1; for(i=1; i<argc; i++){ if( strcmp( argv[i], "-e" ) ==0 || strcmp( argv[i], "--eval" ) ==0 ) break; /* also allows execution of script files without suffix .dao */ if( argv[i][0] != '-' ){ idsrc = i; break; } } #ifdef DAO_WITH_STATIC_MODULES idsrc = 1; vmods = 0; while( dao_virtual_modules[vmods].name ){ DaoVmSpace_AddVirtualModule( vmSpace, & dao_virtual_modules[vmods] ); vmods ++; } #endif k = idsrc; if( k < 0 ) k = argc; opts = DString_New(); args = DString_New(); for(i=1; i<k; i++ ){ DString_AppendChars( opts, argv[i] ); DString_AppendChar( opts, '\1' ); } if( idsrc >= 0 ){ for(i=idsrc; i<argc; i++ ){ DString_AppendChars( args, argv[i] ); DString_AppendChar( args, '\1' ); } } DaoVmSpace_ParseOptions( vmSpace, DString_GetData( opts ) ); #ifdef DAO_WITH_STATIC_MODULES if( vmods ){ DString_InsertChar( args, '\1', 0 ); DString_InsertChars( args, dao_virtual_modules[0].name, 0, 0, 0 ); /* set the path for the virtual files: */ DaoVmSpace_SetPath( vmSpace, "/@/" ); }else #endif if( idsrc < 0 && argc == 1 ){ DString_AppendChar( opts, '\1' ); DString_AppendChars( opts, "-vi" ); DaoVmSpace_ParseOptions( vmSpace, DString_GetData( opts ) ); } #ifdef DAO_USE_READLINE DaoVmSpace_ReadLine( vmSpace, DaoReadLine ); DaoVmSpace_AddHistory( vmSpace, (AddHistory) add_history ); read_history( NULL ); #endif signal( SIGINT, DaoSignalHandler ); /* Start execution. */ k = ! DaoVmSpace_RunMain( vmSpace, DString_GetData( args ) ); #ifdef DAO_USE_READLINE write_history( NULL ); #endif DString_Delete( args ); DString_Delete( opts ); DaoQuit(); return k; }
/* assumed to be called before parsing class body */ void DaoClass_DeriveClassData( DaoClass *self ) { DaoType *type; DaoValue *value; DArray *parents, *offsets; DString *mbs; DNode *it, *search; daoint i, k, id, perm, index; mbs = DString_New(1); if( self->clsType->bases == NULL ) self->clsType->bases = DArray_New(D_VALUE); if( self->objType->bases == NULL ) self->objType->bases = DArray_New(D_VALUE); DArray_Clear( self->clsType->bases ); DArray_Clear( self->objType->bases ); for(i=0; i<self->superClass->size; i++){ if( self->superClass->items.pValue[i]->type == DAO_CLASS ){ DaoValue *klass = self->superClass->items.pValue[i]; DArray_Append( self->clsType->bases, klass->xClass.clsType ); DArray_Append( self->objType->bases, klass->xClass.objType ); DString_Assign( mbs, klass->xClass.className ); DString_AppendChar( mbs, '#' ); DString_AppendInteger( mbs, i+1 ); DaoClass_AddConst( self, mbs, klass, DAO_DATA_PRIVATE ); }else if( self->superClass->items.pValue[i]->type == DAO_CTYPE ){ DaoCtype *cdata = (DaoCtype*) self->superClass->items.pValue[i]; DaoTypeKernel *kernel = cdata->ctype->kernel; DMap *values = kernel->values; DMap *methods = kernel->methods; DArray_Append( self->clsType->bases, cdata->ctype ); DArray_Append( self->objType->bases, cdata->cdtype ); if( values == NULL ){ DaoNamespace_SetupValues( kernel->nspace, kernel->typer ); values = kernel->values; } if( methods == NULL ){ DaoNamespace_SetupMethods( kernel->nspace, kernel->typer ); methods = kernel->methods; } DString_Assign( mbs, cdata->ctype->name ); // XXX DaoClass_AddConst( self, mbs, (DaoValue*)cdata, DAO_DATA_PRIVATE ); DString_AppendChar( mbs, '#' ); DString_AppendInteger( mbs, i+1 ); DaoClass_AddConst( self, mbs, (DaoValue*)cdata, DAO_DATA_PRIVATE ); } } parents = DArray_New(0); offsets = DArray_New(0); DaoClass_Parents( self, parents, offsets ); for(i=1; i<parents->size; i++){ DaoClass *klass = parents->items.pClass[i]; DaoCdata *cdata = parents->items.pCdata[i]; if( klass->type == DAO_CLASS ){ if( klass->vtable ){ if( self->vtable == NULL ) self->vtable = DHash_New(0,0); for(it=DMap_First(klass->vtable); it; it=DMap_Next(klass->vtable,it)){ DMap_Insert( self->vtable, it->key.pVoid, it->value.pVoid ); } } /* For class data: */ for( id=0; id<klass->cstDataName->size; id++ ){ DString *name = klass->cstDataName->items.pString[id]; value = klass->constants->items.pConst[ id ]->value; search = MAP_Find( klass->lookupTable, name ); if( search == NULL ) continue; perm = LOOKUP_PM( search->value.pInt ); /* NO deriving private member: */ if( perm <= DAO_DATA_PRIVATE ) continue; if( value->type == DAO_ROUTINE ){ if( DString_EQ( value->xRoutine.routName, klass->className ) ) continue; } search = MAP_Find( self->lookupTable, name ); if( search == NULL && value->type == DAO_ROUTINE && value->xRoutine.overloads ){ /* To skip the private methods: */ DaoClass_AddConst( self, name, (DaoValue*)value, perm ); }else if( search == NULL ){ index = LOOKUP_BIND( DAO_CLASS_CONSTANT, perm, i+1, self->constants->size ); MAP_Insert( self->lookupTable, name, index ); DArray_Append( self->constants, klass->constants->items.pConst[id] ); DArray_Append( self->cstDataName, (void*)name ); if( value->type == DAO_ROUTINE && (value->xRoutine.attribs & DAO_ROUT_VIRTUAL) ){ if( self->vtable == NULL ) self->vtable = DHash_New(0,0); MAP_Insert( self->vtable, value, value ); } }else if( value->type == DAO_ROUTINE && value->xRoutine.overloads ){ DRoutines *routs = value->xRoutine.overloads; for(k=0; k<routs->routines->size; k++){ DaoRoutine *rout = routs->routines->items.pRoutine[k]; /* skip methods not defined in this parent type */ if( rout->routHost != klass->objType ) continue; if( rout->attribs & DAO_ROUT_PRIVATE ) continue; DaoClass_AddConst( self, name, (DaoValue*)rout, perm ); } }else if( value->type == DAO_ROUTINE ){ /* skip methods not defined in this parent type */ if( value->xRoutine.routHost != klass->objType ) continue; if( value->xRoutine.attribs & DAO_ROUT_PRIVATE ) continue; DaoClass_AddConst( self, name, value, perm ); } } /* class global data */ for( id=0; id<klass->glbDataName->size; id ++ ){ DString *name = klass->glbDataName->items.pString[id]; DaoVariable *var = klass->variables->items.pVar[id]; search = MAP_Find( klass->lookupTable, name ); perm = LOOKUP_PM( search->value.pInt ); /* NO deriving private member: */ if( perm <= DAO_DATA_PRIVATE ) continue; search = MAP_Find( self->lookupTable, name ); /* To overide data: */ if( search == NULL ){ index = LOOKUP_BIND( DAO_CLASS_VARIABLE, perm, i+1, self->variables->size ); MAP_Insert( self->lookupTable, name, index ); DArray_Append( self->variables, var ); DArray_Append( self->glbDataName, (void*)name ); } } }else if( cdata->type == DAO_CTYPE ){ DaoCtype *ctypeobj = (DaoCtype*) cdata; DaoTypeKernel *kernel = cdata->ctype->kernel; DaoTypeBase *typer = kernel->typer; DMap *values = kernel->values; DMap *methods = kernel->methods; DNode *it; int j; DaoCdataType_SpecializeMethods( cdata->ctype ); kernel = cdata->ctype->kernel; methods = kernel->methods; if( typer->numItems ){ for(j=0; typer->numItems[j].name!=NULL; j++){ DString name = DString_WrapMBS( typer->numItems[j].name ); it = DMap_Find( values, & name ); if( it && DMap_Find( self->lookupTable, & name ) == NULL ) DaoClass_AddConst( self, it->key.pString, it->value.pValue, DAO_DATA_PUBLIC ); } } for(it=DMap_First( methods ); it; it=DMap_Next( methods, it )){ DaoRoutine *func = it->value.pRoutine; DaoRoutine **funcs = & func; int k, count = 1; if( it->value.pValue->type == DAO_ROUTINE && it->value.pRoutine->overloads ){ DRoutines *routs = it->value.pRoutine->overloads; funcs = routs->routines->items.pRoutine; count = routs->routines->size; } for(k=0; k<count; k++){ DaoRoutine *func = funcs[k]; if( func->routHost != ctypeobj->cdtype ) continue; if( func->attribs & DAO_ROUT_INITOR ) continue; DaoClass_AddConst( self, it->key.pString, (DaoValue*)func, DAO_DATA_PUBLIC ); } } } } DArray_Delete( parents ); DArray_Delete( offsets ); DString_Delete( mbs ); }
DaoClass* DaoClass_Instantiate( DaoClass *self, DArray *types ) { DaoClass *klass = NULL; DaoType *type; DString *name; DNode *node; DMap *deftypes; daoint lt = DString_FindChar( self->className, '<', 0 ); daoint i, holders = 0; if( self->typeHolders == NULL || self->typeHolders->size ==0 ) return self; while( types->size < self->typeHolders->size ){ type = self->typeDefaults->items.pType[ types->size ]; if( type == NULL ) type = self->typeHolders->items.pType[ types->size ]; DArray_Append( types, type ); } name = DString_New(1); DString_Append( name, self->className ); if( lt != MAXSIZE ) DString_Erase( name, lt, MAXSIZE ); DString_AppendChar( name, '<' ); for(i=0; i<types->size; i++){ type = types->items.pType[i]; holders += type->tid == DAO_THT; if( i ) DString_AppendChar( name, ',' ); DString_Append( name, type->name ); } DString_AppendChar( name, '>' ); while( self->templateClass ) self = self->templateClass; node = DMap_Find( self->instanceClasses, name ); if( node ){ klass = node->value.pClass; }else{ deftypes = DMap_New(0,0); klass = DaoClass_New(); if( holders ) klass->templateClass = self; DMap_Insert( self->instanceClasses, name, klass ); DaoClass_AddReference( self, klass ); /* No need for cleanup of klass; */ DaoClass_SetName( klass, name, self->classRoutine->nameSpace ); for(i=0; i<types->size; i++){ type = types->items.pType[i]; if( DaoType_MatchTo( type, self->typeHolders->items.pType[i], deftypes ) ==0 ){ DString_Delete( name ); return NULL; } MAP_Insert( deftypes, self->typeHolders->items.pVoid[i], type ); } klass->objType->nested = DArray_New(D_VALUE); DArray_Assign( klass->objType->nested, types ); if( DaoClass_CopyField( klass, self, deftypes ) == 0 ){ DString_Delete( name ); return NULL; } DaoClass_DeriveClassData( klass ); DaoClass_DeriveObjectData( klass ); DaoClass_ResetAttributes( klass ); DMap_Delete( deftypes ); if( holders ){ klass->typeHolders = DArray_New(0); klass->typeDefaults = DArray_New(0); klass->instanceClasses = DMap_New(D_STRING,0); DMap_Insert( klass->instanceClasses, klass->className, klass ); for(i=0; i<types->size; i++){ DArray_Append( klass->typeHolders, types->items.pType[i] ); DArray_Append( klass->typeDefaults, NULL ); } for(i=0; i<klass->typeHolders->size; i++){ DaoClass_AddReference( klass, klass->typeHolders->items.pType[i] ); DaoClass_AddReference( klass, klass->typeDefaults->items.pType[i] ); } } } DString_Delete( name ); return klass; }
int main( int argc, char **argv ) { int restart = 0; int i, k, idsrc, vmods = 0; DString *opts = NULL, *args = NULL; /*mtrace(); */ for(i=1; i<argc; i++){ if( strcmp( argv[i], "-r" ) ==0 || strcmp( argv[i], "--restart" ) ==0 ){ restart = i; break; } } if( restart ) DaoRestartRun( argv, argc, restart ); vmSpace = DaoInit( argv[0] ); idsrc = -1; for(i=1; i<argc; i++){ if( strcmp( argv[i], "-e" ) ==0 || strcmp( argv[i], "--eval" ) ==0 ) break; /* also allows execution of script files without suffix .dao */ if( argv[i][0] != '-' ){ idsrc = i; break; } } #ifdef DAO_WITH_STATIC_MODULES /* // For single file deployment. // Identify the script argument, such that the arguments before the script // can be passed in as virtual machine arguments. // Example: ./script --restart script.dao ... */ args = DString_Copy( vmSpace->daoBinFile ); DString_Erase( args, 0, vmSpace->daoBinPath->size ); DString_AppendChars( args, ".dao" ); idsrc = 1; for(i=1; i<argc; i++){ if( strcmp( argv[i], args->chars ) == 0 ){ idsrc = i; break; } } vmods = DaoVmSpace_AddVirtualModules( vmSpace, dao_virtual_modules ); DString_Reset( args, 0 ); #endif k = idsrc; if( k < 0 ) k = argc; if( opts == NULL ) opts = DString_New(); if( args == NULL ) args = DString_New(); for(i=1; i<k; i++ ){ DString_AppendChars( opts, argv[i] ); DString_AppendChar( opts, '\1' ); } if( idsrc >= 0 ){ #ifdef DAO_WITH_STATIC_MODULES idsrc += 1; #endif for(i=idsrc; i<argc; i++ ){ DString_AppendChars( args, argv[i] ); DString_AppendChar( args, '\1' ); } } DaoVmSpace_ParseOptions( vmSpace, DString_GetData( opts ) ); #ifdef DAO_WITH_STATIC_MODULES if( vmods ){ DString_InsertChars( args, "/@/\1", 0, 0, 0 ); DString_InsertChars( args, dao_virtual_modules[0].name, 3, 0, 0 ); /* set the path for the virtual files: */ DaoVmSpace_SetPath( vmSpace, "/@/" ); }else #endif if( idsrc < 0 && argc == 1 ){ DString_AppendChar( opts, '\1' ); DString_AppendChars( opts, "-vi" ); DaoVmSpace_ParseOptions( vmSpace, DString_GetData( opts ) ); } #ifdef DAO_USE_READLINE DaoVmSpace_ReadLine( vmSpace, DaoReadLine ); DaoVmSpace_AddHistory( vmSpace, (AddHistory) add_history ); read_history( NULL ); #endif signal( SIGINT, DaoSignalHandler ); signal( SIGSEGV, DaoStackTrace ); /* Start execution. */ k = DaoVmSpace_RunMain( vmSpace, DString_GetData( args ) ); #ifdef DAO_USE_READLINE write_history( NULL ); #endif DString_Delete( args ); DString_Delete( opts ); DaoQuit(); return k; }