home *** CD-ROM | disk | FTP | other *** search
/ Aminet 18 / aminetcdnumber181997.iso / Aminet / dev / gcc / ixemulsrc.lha / ixemul / library / __load_seg.c < prev    next >
C/C++ Source or Header  |  1996-12-11  |  11KB  |  434 lines

  1. /*
  2.  *  This file is part of ixemul.library for the Amiga.
  3.  *  Copyright (C) 1991, 1992  Markus M. Wild
  4.  *
  5.  *  This library is free software; you can redistribute it and/or
  6.  *  modify it under the terms of the GNU Library General Public
  7.  *  License as published by the Free Software Foundation; either
  8.  *  version 2 of the License, or (at your option) any later version.
  9.  *
  10.  *  This library is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.  *  Library General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU Library General Public
  16.  *  License along with this library; if not, write to the Free
  17.  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *  __load_seg.c,v 1.1.1.1 1994/04/04 04:30:54 amiga Exp
  20.  *
  21.  *  __load_seg.c,v
  22.  * Revision 1.1.1.1  1994/04/04  04:30:54  amiga
  23.  * Initial CVS check in.
  24.  *
  25.  *  Revision 1.5  1992/09/14  01:38:35  mwild
  26.  *  fix a bug with #! expansion (forgot separating /)
  27.  *
  28.  *  Revision 1.4  1992/08/09  20:37:07  amiga
  29.  *  change to use 2.x header files by default
  30.  *
  31.  *  Revision 1.3  1992/07/04  19:02:39  mwild
  32.  *  fix typo, the buffer for interpreter-expansion was ways too small...
  33.  *
  34.  * Revision 1.2  1992/05/22  01:45:00  mwild
  35.  * rewrote interpreter expansion, `should' now work as expected
  36.  *
  37.  * Revision 1.1  1992/05/14  19:55:40  mwild
  38.  * Initial revision
  39.  *
  40.  */
  41.  
  42. #define _KERNEL
  43. #include "ixemul.h"
  44. #include "kprintf.h"
  45.  
  46. #include <ctype.h>
  47. #include <string.h>
  48.  
  49. /* 2.0 support */
  50. #include <utility/tagitem.h>
  51. #include <dos/dostags.h>
  52.  
  53. extern void *kmalloc (size_t size);
  54.  
  55. static struct my_seg *try_load_seg (BPTR lock, char *name, char **args, char *progname);
  56.  
  57. char *basename(char *tmp)
  58. {
  59.   char *cp = rindex (tmp, '/');
  60.  
  61.   if (cp)
  62.     return cp + 1;
  63.   if ((cp = index (tmp, ':')))
  64.     return cp + 1;
  65.   return tmp;
  66. }
  67.  
  68. static struct my_seg *
  69. check_resident (char *tmp)
  70. {
  71.   struct my_seg *res = 0;
  72.   struct Segment *seg;
  73.   char *base;
  74.  
  75.   /* big problem: Commo only stores the bare names in the resident
  76.      list. So we have to truncate to the filename part, and so lose
  77.      the ability to explicitly load the disk version even if a 
  78.      resident version is installed */
  79.  
  80.  
  81.   base = basename(tmp);
  82.   Forbid();
  83.   seg = FindSegment(base, 0, 0);
  84.   if (seg)
  85.     {
  86.       /* strange they didn't provide a function for this... */
  87.       if (seg->seg_UC >= 0) 
  88.         seg->seg_UC++;
  89.     }
  90.   Permit();
  91.  
  92.   if (seg && (res = (struct my_seg *)kmalloc(sizeof(*res) + strlen(tmp) + 1)))
  93.     {
  94.       res->segment = seg->seg_Seg;
  95.       res->type    = RESSEG;
  96.       res->priv    = (u_int)seg;
  97.       res->name    = (char *)&res[1];
  98.       strcpy(res->name, tmp);
  99.     }
  100.   else if (seg)
  101.     {
  102.       Forbid();
  103.       if (seg->seg_UC > 0)
  104.     seg->seg_UC--;
  105.       Permit();
  106.     }
  107.  
  108.   return res;
  109. }
  110.  
  111.  
  112. static struct my_seg *
  113. check_loadseg (char *tmp)
  114. {
  115.   struct my_seg *res = 0;
  116.   BPTR seg;
  117.   int err;
  118.  
  119.   /* Note that LoadSeg returns NULL and sets IoErr() to 0 if the file it
  120.      is trying to load is less than 4 bytes long. */
  121.   seg = LoadSeg (tmp);
  122.   if (seg && (res = kmalloc (sizeof (*res) + strlen(tmp) + 1)))
  123.     {
  124.       res->segment = seg;
  125.       res->type       = LOADSEG;
  126.       res->priv    = seg;
  127.       res->name    = (char *)&res[1];
  128.       strcpy(res->name, tmp);
  129.     }
  130.   else if (seg)
  131.     {
  132.       UnLoadSeg (seg);
  133.       errno = ENOMEM;
  134.       return NULL;
  135.     }
  136.   err = IoErr();
  137.   errno = (err ? __ioerr_to_errno(err) : ENOENT);
  138.  
  139.   if (errno == EINVAL)
  140.     errno = ENOENT;
  141.     
  142.   return res;
  143. }
  144.  
  145.  
  146. void
  147. __free_seg (BPTR *seg)
  148. {
  149.   struct my_seg *ms;
  150.   
  151.   ms = (struct my_seg *) seg;
  152.   if (ms->type == RESSEG)
  153.     {
  154.       struct Segment *s = (struct Segment *) ms->priv;
  155.  
  156.       Forbid ();
  157.       if (s->seg_UC > 0)
  158.     s->seg_UC--;
  159.       Permit ();
  160.     }
  161.   else
  162.     UnLoadSeg (ms->priv);
  163.  
  164.   kfree (ms);
  165. }
  166.  
  167.  
  168. /*
  169.  * This function does what LoadSeg() does, and a little bit more ;-)
  170.  * Besides walking the PATH of the user, we try to do interpreter expansion as
  171.  * well. But, well, we do it a little bit different then a usual Amiga-shell.
  172.  * We check the magic cookies `#!' and `;!', and if found, run the interpreter
  173.  * specified on this first line of text. This does *not* depend on any script
  174.  * bit set!
  175.  */
  176.  
  177. /*
  178.  * IMPORTANT: only call this function with all signals masked!!! 
  179.  */
  180.  
  181. /*
  182.  * name:        the name of the command to load. Can be relative to installed PATH
  183.  * args:        if set, a string to the first part of an expanded command is stored
  184.  */
  185.  
  186. BPTR *
  187. __load_seg (char *name, char **args)
  188. {
  189.   BPTR lock;
  190.   struct my_seg *seg;
  191.  
  192.   /* perhaps the name is vanilla enough, so that even LoadSeg() groks it? */
  193.   if (args) *args = 0;
  194.  
  195.   seg = check_resident (name);
  196.  
  197.   if (! seg)
  198.     seg = check_loadseg (name);
  199.  
  200.   if (seg)
  201.     return &seg->segment;
  202.  
  203.   if (errno != ENOENT)
  204.     return 0;
  205.  
  206.   /* try to lock the file (using __lock() provides full path-parsing ;-)) */
  207.   lock = __lock (name, ACCESS_READ);
  208.   if (lock)
  209.     {
  210.       int err;
  211.       BPTR parent = ParentDir(lock);
  212.       struct FileInfoBlock *fib;
  213.  
  214.       fib = alloca (sizeof(*fib) + 2);
  215.       fib = LONG_ALIGN (fib);
  216.       if (Examine (lock, fib))
  217.         seg = try_load_seg (parent, fib->fib_FileName, args, name);
  218.       else
  219.         seg = try_load_seg (parent, basename(name), args, name);
  220.       err = errno;
  221.  
  222.       __unlock (parent);
  223.       __unlock (lock);
  224.  
  225.       errno = err;
  226.       if (!seg && errno != ENOENT)
  227.         return 0;
  228.     }
  229.  
  230.   /* now we may have a valid segment */
  231.   if (seg)
  232.     return &seg->segment;
  233.  
  234.   /* if the command was specified with some kind of path, for example with a
  235.    * device or a directory in it, we don't run it thru the PATH expander
  236.    */
  237.   if (strpbrk (name, ":/"))
  238.     {
  239.       errno = ENOENT;
  240.       return 0;
  241.     }
  242.  
  243.   /* so the command is not directly addressable, but perhaps it's in our PATH? */
  244.   {
  245.     struct Process *me = (struct Process *)(SysBase->ThisTask);
  246.     struct CommandLineInterface *cli;
  247.  
  248.     /* but we need a valid CLI then */
  249.     if ((cli = BTOCPTR (me->pr_CLI)))
  250.       {
  251.     struct path_element {
  252.       BPTR    next;
  253.       BPTR     lock;
  254.     } *lock_list;
  255.     BPTR ocd;
  256.  
  257.     for (lock_list = BTOCPTR (cli->cli_CommandDir);
  258.          lock_list;
  259.          lock_list = BTOCPTR (lock_list->next))
  260.       {
  261.             ocd = CurrentDir (lock_list->lock);
  262.         seg = try_load_seg (lock_list->lock, name, args, name);
  263.         CurrentDir(ocd);
  264.         if (seg)
  265.           break;
  266.         if (errno != ENOENT)
  267.               return 0;
  268.       }
  269.       }
  270.   }
  271.   
  272.   if (seg)
  273.     return &seg->segment;
  274.  
  275.   errno = ENOENT;
  276.   KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  277.   return 0;
  278. }
  279.  
  280. static struct my_seg *
  281. try_load_seg (BPTR parent, char *name, char **args, char *progname)
  282. {
  283.   BPTR ocd, tmpcd;
  284.   short oroot;
  285.   struct my_seg *seg;
  286.  
  287.   if (args) *args = 0;
  288.   ocd = CurrentDir (parent);
  289.   oroot = u.u_is_root;
  290.   u.u_is_root = 0;
  291.   
  292.   seg = check_loadseg (name);
  293.  
  294.   if (!seg && errno != ENOENT)
  295.     {
  296.       CurrentDir(ocd);
  297.       u.u_is_root = oroot;
  298.       return 0;
  299.     }
  300.  
  301.   /* try to do interpreter - expansion, but only if args is non-zero */
  302.   if (!seg && args)
  303.     {
  304.       int fd, n;
  305.       char magic[5];
  306.       struct stat stb;
  307.  
  308.       if (syscall (SYS_stat, name, &stb) == 0 && S_ISREG (stb.st_mode))
  309.     {
  310.       if ((fd = syscall (SYS_open, name, 0)) >= 0)
  311.             {
  312.               /*
  313.                *  If the .key line of an AmigaOS script isn't the first
  314.                *  line of the script, the AmigaOS shell gets very confused.
  315.                *  Therefore, we skip the first line if it begins with .key,
  316.                *  and we test the second line for #! or ;!.
  317.                */
  318.           if ((n = syscall (SYS_read, fd, magic, 4)) == 4)
  319.             {
  320.               magic[4] = 0;
  321.               if (!strcasecmp(magic, ".key"))
  322.                 {
  323.                   n = 0;
  324.                   /* skip this line */
  325.                   while (syscall (SYS_read, fd, magic, 1) == 1)
  326.                     if (magic[0] == '\n')
  327.                       {
  328.                         n = syscall (SYS_read, fd, magic, 4);
  329.                         break;
  330.                       }
  331.                 }
  332.             }
  333.           if (n >= 2 && (magic[0] == '#' || magic[0] == ';') && magic[1] == '!')
  334.             {
  335.               char interp[MAXPATHLEN + 1], *interp_start;
  336.           
  337.               interp[0] = magic[2];
  338.               interp[1] = magic[3];
  339.               n -= 2;
  340.               n = n + syscall (SYS_read, fd, interp + n, MAXINTERP - n);
  341.               if (n > 0)
  342.             {
  343.               char *cp, ch;
  344.               char *interp_path;
  345.  
  346.               /* okay.. got one.. terminate with 0 and try to find end of it */
  347.               interp[n] = 0;
  348.               for (interp_start = interp; isspace(*interp_start) && interp_start < interp + n; interp_start++);
  349.               for (cp = interp_start; cp < interp + n; cp++)
  350.                 if (*cp == 0 || isspace (*cp)) break;
  351.               ch = *cp;
  352.               *cp = 0;
  353.  
  354.               /* okay, lets try to load this instead. Call us recursively,
  355.                * but leave out the argument-argument, so we can't get
  356.                * into infinite recursion. Interpreter-Expansion is only
  357.                * done the first time __load_seg() is called from
  358.                * execve()
  359.                */
  360.                       tmpcd = CurrentDir(ocd);
  361.               u.u_is_root = oroot;
  362.               seg = (struct my_seg *) __load_seg (interp_start, 0);
  363.                       CurrentDir(tmpcd);
  364.               u.u_is_root = 0;
  365.               *cp = ch;
  366.               if (!seg)
  367.                 goto fd_close;
  368.           
  369.               /* in this case, set the argument as well. 
  370.                */
  371.  
  372.               /* first skip intergap whitespace */
  373.               for (; cp < interp + n; cp++)
  374.             if (!*cp || !isspace(*cp) || *cp == '\n')
  375.               break;
  376.  
  377.               if (*cp && *cp != '\n')
  378.                 {
  379.               /* we read a certain amount of bytes, but we 
  380.                * unconditionally stop when we hit newline
  381.                */
  382.               interp_path = cp;
  383.  
  384.                   /* crop any further arguments, only ONE argument
  385.                    * is supported
  386.                */
  387.                   for (; cp < interp + n; cp++) 
  388.                 if (isspace (*cp)) 
  389.                   break;
  390.                   if (cp < interp + n)
  391.                 *cp = 0;
  392.  
  393.               *cp++ = ' ';
  394.             }
  395.               else
  396.             cp = interp_path = interp_start;
  397.  
  398.               if (progname[0] != '/' && !index(progname, ':'))
  399.             {
  400.                           BPTR lock;
  401.  
  402.                           CurrentDir (ocd);
  403.                           lock = __llock (progname, ACCESS_READ);
  404.               *cp++ = '/';
  405.               if (lock && NameFromLock (lock, cp, 
  406.                         MAXPATHLEN-(cp-interp)) == -1)
  407.                 {
  408.                   if ((cp = index(cp, ':')))
  409.                     *cp = '/';
  410.                 }
  411.               else
  412.                 strcpy (cp - 1, progname);
  413.                           if (lock)
  414.                             __unlock (lock);
  415.             }
  416.               else
  417.             strcpy (cp, progname);
  418.  
  419.               *args = (char *) syscall (SYS_strdup, interp_path);
  420.             }
  421.             }
  422. fd_close:
  423.           syscall (SYS_close, fd);
  424.         }
  425.         }
  426.     }
  427.   CurrentDir (ocd);
  428.   u.u_is_root = oroot;
  429.  
  430.   if (!seg)  
  431.     errno = ENOENT;
  432.   return seg;
  433. }
  434.