/* * PARSE1.C * * (c)Copyright 1993-2014, Matthew Dillon, All Rights Reserved. See the * COPYRIGHT file at the base of the distribution. */ #include "defs.h" #include static ipnodelist_t ImportPathList = RUNE_HEAD_INITIALIZER(ImportPathList); static void DelImportPath(IPNode *ip); static const char *temporaryImportPath(Stmt *st); /* * Open the (possibly shared) parser associated with the ImportStmt, * assigning and returning the parser. The first token of the file is * retrieved and returned, or an appropriate TOK_ERR_* is returned. * * XXX locate an already-existant shared parser structure and use directly */ int ParseOpen(Stmt *st, Parse **pp, void **pdll) { Parse *p = zalloc(sizeof(Parse)); IPNode last_sip; /* backup relative path */ IPNode *sip; int t = 0; /* * Insert the current path at the head so relative imports always work. */ dassert_stmt(st, st->st_Op == ST_Import); p->p_IPath.ip_Flags |= IPF_STATIC; p->p_IPath.ip_Path = safe_strdup(temporaryImportPath(st->st_Parent)); p->p_Format = PFMT_SOURCE; RUNE_INSERT_HEAD(&ImportPathList, &p->p_IPath, ip_Node); /* * last_sip iterates parent directories for convenience so the programmer * doesn't have to use '..' all over the place. */ bzero(&last_sip, sizeof(last_sip)); last_sip.ip_Flags |= IPF_STATIC; /* * Scan import paths looking for the import */ sip = RUNE_FIRST(&ImportPathList); for (;;) { char *npath; char *pathdir = NULL; int r; char mode = 'd'; SemGroup *sg2; /* * Once we exhaust the list try iterating up "..". * * NOTE: Do not 'optimize' ".." by removing tail path elements, this * will break softlinks. */ if (sip == NULL) { sip = &last_sip; if (strcmp(p->p_IPath.ip_Path, "./") == 0) asprintf(&sip->ip_Path, "../"); else asprintf(&sip->ip_Path, "%s../", p->p_IPath.ip_Path); } else if (sip == &last_sip) { struct stat st1; struct stat st2; stat(sip->ip_Path, &st1); asprintf(&npath, "%s../", sip->ip_Path); free(sip->ip_Path); sip->ip_Path = npath; stat(sip->ip_Path, &st2); /* * Detect the system root (it is ok to cross mount boundaries). */ if (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev) { break; } } if (sip == &last_sip && sip->ip_Path == NULL) break; /* * Construct a candidate path. If the path is a directory, tack on a * "/main.d". If the path is relative, do it relative to the current * source file's location. If the path is absolute, ignore sip. */ if (st->st_ImportStmt.es_Path[0] == '/') safe_asprintf(&npath, "%s%s", st->st_ImportStmt.es_Path, st->st_ImportStmt.es_File); else if (sip->ip_Flags & (IPF_STATIC | IPF_ABSOLUTE)) safe_asprintf(&npath, "%s%s%s", sip->ip_Path, st->st_ImportStmt.es_Path, st->st_ImportStmt.es_File); else safe_asprintf(&npath, "%s%s%s%s", p->p_IPath.ip_Path, sip->ip_Path, st->st_ImportStmt.es_Path, st->st_ImportStmt.es_File); /* * Locate the file, adjust for directories */ r = stat(npath, &p->p_St); if (r == 0 && S_ISDIR(p->p_St.st_mode)) { pathdir = npath; safe_asprintf(&npath, "%s/main.d", npath); r = stat(npath, &p->p_St); } /* * Figure out the extension. XXX convert the fatal error into a * TOK_ERR... */ if (pathdir == NULL) { const char *ext = strrchr(npath, '.'); if (ext && strlen(ext) <= 3) { ++ext; if (strcmp(ext, "d") == 0) mode = 'd'; else if (strcmp(ext, "r") == 0) mode = 'r'; else if (strcmp(ext, "so") == 0) mode = 'r'; else if (strcmp(ext, "a") == 0) mode = 'r'; else dfatal("Unknown import extension on %s\n", npath); } } /* * SHARED RUNE SOURCE MODULE * * If we have already imported this and it is shared, set STF_SHARED * and set es_Decl and st_MyGroup and do not open the file. */ if ((mode == 'd' || mode == 'r') && r == 0 && (sg2 = GetSharedImport(&p->p_St)) != NULL ) { p->p_Import = st; st->st_Flags |= STF_SHARED; st->st_ImportStmt.es_SemGroup = sg2; if (pathdir) { safe_replacef(&st->st_ImportStmt.es_Path, "%s/", pathdir); safe_replace(&st->st_ImportStmt.es_File, "main.d"); safe_free(&pathdir); } safe_replace(&p->p_IPath.ip_Path, st->st_ImportStmt.es_Path); dassert_stmt(st, sg2->sg_Stmt->st_Op == ST_Module); if (pdll) *pdll = sg2->sg_Stmt->st_ImportStmt.es_DLL; t = 0; break; } /* * RUNE SOURCE MODULE TO PARSE * * Attempt to open the path. If we succeed we have to do some fixups * for the directory case. */ if (mode == 'd' && r == 0 && (p->p_Lex = LexOpen(npath, &p->p_Token)) != NULL) { /* * note that st_ImportStmt.es_SemGroup is not yet known. The * ParseModule() code will deal with it. */ st->st_ImportStmt.es_Parser = p; p->p_Import = st; p->p_CurStmt = st; if (pathdir) { safe_replacef(&st->st_ImportStmt.es_Path, "%s/", pathdir); safe_replace(&st->st_ImportStmt.es_File, "main.d"); safe_free(&pathdir); } safe_replace(&p->p_IPath.ip_Path, st->st_ImportStmt.es_Path); /* * Inherit the declaration block of the caller and prime the * first token. */ p->p_CurSemGroup = st->st_MyGroup; t = LexToken(&p->p_Token); break; } /* * DYNAMIC LINK LIBRARY * * This is typically used to add system and library calls to the * language run-time. */ if (mode == 'r' && r == 0) { void *dll = dlopen(npath, RTLD_NOW | RTLD_LOCAL); if (dll != NULL) { void (*init) (void)= dlsym(dll, "init"); if (init) init(); p->p_Import = st; st->st_ImportStmt.es_SemGroup = NULL; p->p_Import = st; p->p_CurStmt = st; st->st_Flags |= STF_SHARED; /* * XXX should really make the group NULL here, but then the * GetSharedImport check will not work. XXX */ SetSharedImport(&p->p_St, st->st_MyGroup); if (pathdir) { safe_replacef( &st->st_ImportStmt.es_Path, "%s/", pathdir); safe_replace( &st->st_ImportStmt.es_File, "main.r"); safe_free(&pathdir); } safe_replace( &p->p_IPath.ip_Path, st->st_ImportStmt.es_Path); t = 0; if (pdll) *pdll = dll; break; } else { fprintf(stderr, "UNABLE TO LOAD DLL: %s %s\n", npath, dlerror()); } } safe_free(&pathdir); ParseDone(p); st->st_ImportStmt.es_Parser = NULL; t = TOK_ERR_IMPORT_NOT_FOUND; /* * If the import statement is using an absolute path itself, we * ignore 'sip' and so there is no need to loop. */ if (st->st_ImportStmt.es_Path[0] == '/') break; if (sip != &last_sip) sip = RUNE_NEXT(sip, ip_Node); } if (last_sip.ip_Path) { free(last_sip.ip_Path); last_sip.ip_Path = NULL; /* safety */ } DelImportPath(&p->p_IPath); *pp = p; if (t & TOKF_ERROR) ParseClose(pp); return (t); } /* * XXX what to do here? */ void ParseDone(Parse *p) { if (p) LexClose(&p->p_Lex); } void ParseClose(Parse **pp) { Parse *p; if ((p = *pp) != NULL) { *pp = NULL; LexClose(&p->p_Lex); /* XXX free stuff up */ zfree_wipe(p, sizeof(Parse)); } } void AddImportPath(const char *path, int flags) { IPNode *ip = zalloc(sizeof(IPNode)); safe_asprintf(&ip->ip_Path, "%s/", path); RUNE_INSERT_TAIL(&ImportPathList, ip, ip_Node); ip->ip_Flags |= flags; if (path[0] == '/') ip->ip_Flags |= IPF_ABSOLUTE; } static void DelImportPath(IPNode *ip) { if (ip->ip_Path) { safe_free(&ip->ip_Path); RUNE_REMOVE(&ImportPathList, ip, ip_Node); } if ((ip->ip_Flags & IPF_STATIC) == 0) zfree(ip, sizeof(IPNode)); } void PrefixImportPaths(const char *basePath) { IPNode *ip; RUNE_FOREACH(ip, &ImportPathList, ip_Node) { if (ip->ip_Flags & IPF_BASEPATH) { safe_replacef(&ip->ip_Path, "%s%s", basePath, ip->ip_Path); } if (basePath[0] == '/') ip->ip_Flags |= IPF_ABSOLUTE; } } static const char * temporaryImportPath(Stmt *st) { while (st) { if (st->st_Op == ST_Import) return (st->st_ImportStmt.es_Path); st = st->st_Parent; } return (""); } struct ImportShare; typedef RUNE_HEAD(ipslist, ImportShare) ipslist_t; typedef struct ImportShare { RUNE_ENTRY(ImportShare) is_Node; dev_t is_RDev; ino_t is_Ino; SemGroup *is_SemGroup; } ImportShare; static ipslist_t ISList = RUNE_HEAD_INITIALIZER(ISList); void SetSharedImport(struct stat *st, SemGroup *sg) { ImportShare *is = zalloc(sizeof(ImportShare)); RUNE_INSERT_TAIL(&ISList, is, is_Node); is->is_RDev = st->st_rdev; is->is_Ino = st->st_ino; is->is_SemGroup = sg; } SemGroup * GetSharedImport(struct stat *st) { ImportShare *is; RUNE_FOREACH(is, &ISList, is_Node) { if (st->st_ino == is->is_Ino && st->st_rdev == is->is_RDev) return (is->is_SemGroup); } return (NULL); }