Files
2015-04-16 09:16:48 +02:00

506 lines
12 KiB
C

/*
* This file is part of ixemul.library for the Amiga.
* Copyright (C) 1991, 1992 Markus M. Wild
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: __load_seg.c,v 1.2 2007/03/19 17:40:09 piru Exp $
*/
/* This module implements:
* - basename(fullpath) -> filename
* - loadseg() functions
* __load_seg(name, args):
* magic load_seg, with script-sniffing, stashes clean-up information
* in the `my_seg structure (see ixemul.h)
* __free_seg(seg):
* `unload' a segment obtained through __load_seg
*/
#define _KERNEL
#include "ixemul.h"
#include "kprintf.h"
#include <ctype.h>
#include <string.h>
/* 2.0 support */
#include <utility/tagitem.h>
#include <dos/dostags.h>
extern void *kmalloc (size_t size);
extern char *ix_to_ados(char *buf, const char *name);
extern char *check_root(char *);
static struct my_seg *load_seg_or_script (BPTR lock, const char *name, const char *adosname, char **args, const char *progname);
char *basename(char *tmp)
{
char *cp = rindex (tmp, '/');
if (cp)
return cp + 1;
if ((cp = index (tmp, ':')))
return cp + 1;
return tmp;
}
/* dirlock(name) -> lock on that filename's directory */
static BPTR
dirlock (const char *tmp)
{
BPTR dir;
char *cp;
char o;
/* explicit lock for a complex pathname */
cp = rindex (tmp, '/');
if (cp)
{
*cp = 0;
dir = Lock(tmp, ACCESS_READ);
*cp = '/';
}
else {
cp = rindex (tmp, ':');
if (cp)
{
o = cp[1];
cp[1] = 0;
dir = Lock(tmp, ACCESS_READ);
cp[1] = o;
}
else { /* duplicate the current directory lock for a simple pathname */
BPTR orig_dir = CurrentDir(NULL);
dir = DupLock(orig_dir);
CurrentDir(orig_dir);
}
}
return dir;
}
/* priv_seg = check_and_load_resident (char *filename):
* `loadseg' a program on the resident list, setting up
* the private segment information for easy unloading
*/
static struct my_seg *
check_and_load_resident (const char *filename)
{
struct my_seg *res = 0;
struct Segment *seg;
const char *base;
/* big problem: Commo only stores the bare names in the resident
list. So we have to truncate to the filename part, and so lose
the ability to explicitly load the disk version even if a
resident version is installed */
base = basename((char *) filename);
Forbid();
seg = FindSegment(base, 0, 0);
if (seg)
{
/* strange they didn't provide a function for this... */
if (seg->seg_UC >= 0)
seg->seg_UC++;
}
Permit();
if (seg) /* ok, we have the seg */
{
KPRINTF(("__loadseg: \"%s\" is resident.\n", filename));
if ((res = (struct my_seg *)kmalloc(sizeof(*res) + strlen(filename) + 1)))
{
res->segment = seg->seg_Seg;
res->type = RESSEG;
res->priv = (u_int)seg;
res->programdir = NULL; /* no possible programdir as Commo didn't provide for it */
res->name = (char *)&res[1];
strcpy(res->name, filename);
}
else
{ /* oops, we can't deal with it */
Forbid();
if (seg->seg_UC > 0)
seg->seg_UC--;
Permit();
}
}
return res;
}
/* really loadseg from disk */
static struct my_seg *
check_and_load_seg (const char *filename)
{
struct my_seg *res = 0;
BPTR seg;
int err;
usetup;
/* Note that LoadSeg returns NULL and sets IoErr() to 0 if the file it
is trying to load is less than 4 bytes long. */
seg = LoadSeg (filename);
if (seg)
{
if ((res = kmalloc (sizeof (*res) + strlen(filename) + 1)))
{
res->segment = seg;
res->type = LOADSEG;
res->priv = seg;
res->programdir = dirlock(filename);
res->name = (char *)&res[1];
strcpy(res->name, filename);
}
else
{
UnLoadSeg (seg);
errno = ENOMEM;
return NULL;
}
}
err = IoErr();
errno = (err ? __ioerr_to_errno(err) : ENOENT);
if (errno == ENOTDIR || errno == EFTYPE)
errno = ENOENT;
return res;
}
void
__free_seg (BPTR *seg)
{
struct my_seg *ms;
ms = (struct my_seg *) seg;
switch (ms->type)
{
case RESSEG:
{
struct Segment *s = (struct Segment *) ms->priv;
Forbid ();
if (s->seg_UC > 0)
s->seg_UC--;
Permit ();
}
break;
case LOADSEG:
UnLoadSeg (ms->priv);
break;
default: /* unknown segment type -> this is not a struct my_seg ! */
ix_panic("Corrupted segment");
break;
}
/* we could unlock the programdir before, but this way, we gain the `dynamic
* type check' that this is truely a struct my_seg.
* Also note that __unlock() can deal with NULL locks */
UnLock (ms->programdir);
kfree (ms);
}
/*
* This function does what LoadSeg() does, and a little bit more ;-)
* Besides walking the PATH of the user, we try to do interpreter expansion as
* well. But, well, we do it a little bit different then a usual Amiga-shell.
* We check the magic cookies `#!' and `;!', and if found, run the interpreter
* specified on this first line of text. This does *not* depend on any script
* bit set!
*/
/*
* IMPORTANT: only call this function with all signals masked!!!
*/
/*
* name: the name of the command to load. Can be relative to installed PATH
* args: if set, a string to the first part of an expanded command is stored
*/
BPTR *
__load_seg (char *name, char **args)
{
BPTR lock;
struct my_seg *seg;
usetup;
char *buf = alloca(strlen(name) + 4);
char *adosname;
adosname = ix_to_ados(buf, name);
/* perhaps the name is vanilla enough, so that even LoadSeg() groks it? */
if (args) *args = 0;
seg = check_and_load_resident (adosname);
if (!seg)
{
/*
* If the currentdir is / and the commandname is relative,
* try path right away without bothering with currentdir. - Piru
*/
if (u.u_is_root && !strpbrk (name, ":/"))
goto trypath;
seg = check_and_load_seg (adosname);
}
if (!seg)
{
LONG err = IoErr();
if (err == ERROR_OBJECT_NOT_FOUND)
{
char *adosname2 = check_root(adosname);
if (adosname2 && *adosname2)
{
adosname = adosname2;
seg = check_and_load_seg(adosname);
if (seg)
err = 0;
}
SetIoErr(err);
}
}
if (seg)
return (BPTR *)&seg->segment;
if (errno != ENOENT)
return 0;
/* try to lock the file */
lock = Lock(adosname, ACCESS_READ);
if (lock)
{
int err;
BPTR parent = ParentDir(lock);
struct FileInfoBlock *fib;
fib = alloca (sizeof(*fib) + 2);
fib = LONG_ALIGN (fib);
if (Examine (lock, fib))
seg = load_seg_or_script (parent, fib->fib_FileName, fib->fib_FileName, args, name);
else
{
char *base = basename(name);
seg = load_seg_or_script (parent, base, base, args, name);
}
err = errno;
UnLock (parent);
UnLock (lock);
errno = err;
if (!seg && errno != ENOENT)
return 0;
}
/* now we may have a valid segment */
if (seg)
return (BPTR *)&seg->segment;
/* if the command was specified with some kind of path, for example with a
* device or a directory in it, we don't run it thru the PATH expander
*/
if (strpbrk (name, ":/"))
{
errno = ENOENT;
return 0;
}
trypath:
/* so the command is not directly addressable, but perhaps it's in our PATH? */
{
struct Process *me = (struct Process *)SysBase->ThisTask;
struct CommandLineInterface *cli;
/* but we need a valid CLI then */
if ((cli = BTOCPTR (me->pr_CLI)))
{
struct path_element {
BPTR next;
BPTR lock;
} *lock_list;
char progname[MAXPATHLEN];
for (lock_list = BTOCPTR (cli->cli_CommandDir);
lock_list;
lock_list = BTOCPTR (lock_list->next))
{
if (NameFromLock(lock_list->lock, progname, MAXPATHLEN) &&
AddPart(progname, name, MAXPATHLEN))
{
seg = load_seg_or_script (lock_list->lock, name, name, args, progname);
}
else
errno = ENOENT;
if (seg)
break;
if (errno != ENOENT)
return 0;
}
}
}
if (seg)
return (BPTR *)&seg->segment;
errno = ENOENT;
KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
return 0;
}
static struct my_seg *
load_seg_or_script (BPTR parent, const char *name, const char *adosname, char **args, const char *progname)
{
BPTR ocd, tmpcd;
short oroot;
struct my_seg *seg;
usetup;
if (args) *args = 0;
ocd = CurrentDir (parent);
oroot = u.u_is_root;
u.u_is_root = 0;
seg = check_and_load_seg (adosname);
if (!seg && errno != ENOENT)
{
CurrentDir(ocd);
u.u_is_root = oroot;
return 0;
}
/* try to do interpreter - expansion, but only if args is non-zero */
if (!seg && args)
{
int fd, n;
char magic[5];
struct stat stb;
if (syscall (SYS_stat, name, &stb) == 0 && S_ISREG (stb.st_mode))
{
if ((fd = syscall (SYS_open, name, 0)) >= 0)
{
/*
* If the .key line of an AmigaOS script isn't the first
* line of the script, the AmigaOS shell gets very confused.
* Therefore, we skip the first line if it begins with .key,
* and we test the second line for #! or ;!.
*/
if ((n = syscall (SYS_read, fd, magic, 4)) == 4)
{
magic[4] = 0;
if (!strcasecmp(magic, ".key"))
{
n = 0;
/* skip this line */
while (syscall (SYS_read, fd, magic, 1) == 1)
if (magic[0] == '\n')
{
n = syscall (SYS_read, fd, magic, 4);
break;
}
}
}
if (n >= 2 && (magic[0] == '#' || magic[0] == ';') && magic[1] == '!')
{
char interp[MAXINTERP + MAXPATHLEN + 2], *interp_start;
interp[0] = magic[2];
interp[1] = magic[3];
n -= 2;
n = n + syscall (SYS_read, fd, interp + n, MAXINTERP - n);
if (n > 0)
{
char *cp, *cp2, ch;
/* okay.. got one.. terminate with 0 and try to find end of it */
interp[n] = 0;
for (interp_start = interp; isspace(*interp_start) && interp_start < interp + n; interp_start++);
for (cp = interp_start; cp < interp + n; cp++)
if (*cp == 0 || isspace (*cp))
break;
ch = *cp;
*cp = 0;
/* okay, lets try to load this instead. Call us recursively,
* but leave out the argument-argument, so we can't get
* into infinite recursion. Interpreter-Expansion is only
* done the first time __load_seg() is called from
* execve()
*/
tmpcd = CurrentDir(ocd);
u.u_is_root = oroot;
seg = (struct my_seg *) __load_seg (interp_start, 0);
CurrentDir(tmpcd);
u.u_is_root = 0;
if (!seg)
goto fd_close;
/* using \001 as seperator for progam|argument|script */
*cp++ = '\001';
if (ch && ch != '\n' && cp < interp + n)
{
cp2 = cp;
/* first skip intergap whitespace */
while (isspace(*cp2) && *cp2 != '\n') cp2++;
/* in this case, set the argument as well. */
if (*cp2 && *cp2 != '\n')
{
while (*cp2 && !isspace(*cp2) && *cp2 != '\n' && cp < interp + n)
*cp++ = *cp2++;
*cp++ = '\001';
}
}
if (index (progname, ':'))
{
*cp++ = '/';
strcpy (cp, progname);
*index (cp, ':') = '/';
}
else
strcpy (cp, progname);
*args = (char *) syscall (SYS_strdup, interp_start);
}
}
fd_close:
syscall (SYS_close, fd);
}
}
}
CurrentDir (ocd);
u.u_is_root = oroot;
if (!seg)
errno = ENOENT;
return seg;
}