(root)/
make-4.4/
src/
ar.c
       1  /* Interface to 'ar' archives for GNU Make.
       2  Copyright (C) 1988-2022 Free Software Foundation, Inc.
       3  
       4  This file is part of GNU Make.
       5  
       6  GNU Make is free software; you can redistribute it and/or modify it under the
       7  terms of the GNU General Public License as published by the Free Software
       8  Foundation; either version 3 of the License, or (at your option) any later
       9  version.
      10  
      11  GNU Make is distributed in the hope that it will be useful, but WITHOUT ANY
      12  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      13  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      14  
      15  You should have received a copy of the GNU General Public License along with
      16  this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include "makeint.h"
      19  
      20  #ifndef NO_ARCHIVES
      21  
      22  #include "filedef.h"
      23  #include "dep.h"
      24  #include <fnmatch.h>
      25  #include <intprops.h>
      26  
      27  /* Return nonzero if NAME is an archive-member reference, zero if not.  An
      28     archive-member reference is a name like 'lib(member)' where member is a
      29     non-empty string.
      30     If a name like 'lib((entry))' is used, a fatal error is signaled at
      31     the attempt to use this unsupported feature.  */
      32  
      33  int
      34  ar_name (const char *name)
      35  {
      36    const char *p = strchr (name, '(');
      37    const char *end;
      38  
      39    if (p == NULL || p == name)
      40      return 0;
      41  
      42    end = p + strlen (p) - 1;
      43    if (*end != ')' || end == p + 1)
      44      return 0;
      45  
      46    if (p[1] == '(' && end[-1] == ')')
      47      OS (fatal, NILF, _("attempt to use unsupported feature: '%s'"), name);
      48  
      49    return 1;
      50  }
      51  
      52  
      53  /* Parse the archive-member reference NAME into the archive and member names.
      54     Creates one allocated string containing both names, pointed to by ARNAME_P.
      55     MEMNAME_P points to the member.  */
      56  
      57  void
      58  ar_parse_name (const char *name, char **arname_p, char **memname_p)
      59  {
      60    char *p;
      61  
      62    *arname_p = xstrdup (name);
      63    p = strchr (*arname_p, '(');
      64    /* This is never called unless ar_name() is true so p cannot be NULL.  */
      65    if (!p)
      66      OS (fatal, NILF, "Internal: ar_parse_name: bad name '%s'", *arname_p);
      67    *(p++) = '\0';
      68    p[strlen (p) - 1] = '\0';
      69    *memname_p = p;
      70  }
      71  
      72  
      73  /* This function is called by 'ar_scan' to find which member to look at.  */
      74  
      75  /* ARGSUSED */
      76  static intmax_t
      77  ar_member_date_1 (int desc UNUSED, const char *mem, int truncated,
      78                    long int hdrpos UNUSED, long int datapos UNUSED,
      79                    long int size UNUSED, intmax_t date,
      80                    int uid UNUSED, int gid UNUSED, unsigned int mode UNUSED,
      81                    const void *name)
      82  {
      83    return ar_name_equal (name, mem, truncated) ? date : 0;
      84  }
      85  
      86  /* Return the modtime of NAME.  */
      87  
      88  time_t
      89  ar_member_date (const char *name)
      90  {
      91    char *arname;
      92    char *memname;
      93    intmax_t val;
      94  
      95    ar_parse_name (name, &arname, &memname);
      96  
      97    /* Make sure we know the modtime of the archive itself because we are
      98       likely to be called just before commands to remake a member are run,
      99       and they will change the archive itself.
     100  
     101       But we must be careful not to enter_file the archive itself if it does
     102       not exist, because pattern_search assumes that files found in the data
     103       base exist or can be made.  */
     104    {
     105      struct file *arfile;
     106      arfile = lookup_file (arname);
     107      if (arfile == 0 && file_exists_p (arname))
     108        arfile = enter_file (strcache_add (arname));
     109  
     110      if (arfile != 0)
     111        (void) f_mtime (arfile, 0);
     112    }
     113  
     114    val = ar_scan (arname, ar_member_date_1, memname);
     115  
     116    free (arname);
     117  
     118    return 0 < val && val <= TYPE_MAXIMUM (time_t) ? val : -1;
     119  }
     120  
     121  /* Set the archive-member NAME's modtime to now.  */
     122  
     123  #ifdef VMS
     124  int
     125  ar_touch (const char *name)
     126  {
     127    O (error, NILF, _("touch archive member is not available on VMS"));
     128    return -1;
     129  }
     130  #else
     131  int
     132  ar_touch (const char *name)
     133  {
     134    char *arname, *memname;
     135    int val;
     136  
     137    ar_parse_name (name, &arname, &memname);
     138  
     139    /* Make sure we know the modtime of the archive itself before we
     140       touch the member, since this will change the archive modtime.  */
     141    {
     142      struct file *arfile;
     143      arfile = enter_file (strcache_add (arname));
     144      f_mtime (arfile, 0);
     145    }
     146  
     147    val = 1;
     148    switch (ar_member_touch (arname, memname))
     149      {
     150      case -1:
     151        OS (error, NILF, _("touch: Archive '%s' does not exist"), arname);
     152        break;
     153      case -2:
     154        OS (error, NILF, _("touch: '%s' is not a valid archive"), arname);
     155        break;
     156      case -3:
     157        perror_with_name ("touch: ", arname);
     158        break;
     159      case 1:
     160        OSS (error, NILF,
     161             _("touch: Member '%s' does not exist in '%s'"), memname, arname);
     162        break;
     163      case 0:
     164        val = 0;
     165        break;
     166      default:
     167        OS (error, NILF,
     168            _("touch: Bad return code from ar_member_touch on '%s'"), name);
     169      }
     170  
     171    free (arname);
     172  
     173    return val;
     174  }
     175  #endif /* !VMS */
     176  
     177  /* State of an 'ar_glob' run, passed to 'ar_glob_match'.  */
     178  
     179  /* On VMS, (object) modules in libraries do not have suffixes. That is, to
     180     find a match for a pattern, the pattern must not have any suffix. So the
     181     suffix of the pattern is saved and the pattern is stripped (ar_glob).
     182     If there is a match and the match, which is a module name, is added to
     183     the chain, the saved suffix is added back to construct a source filename
     184     (ar_glob_match). */
     185  
     186  struct ar_glob_state
     187    {
     188      const char *arname;
     189      const char *pattern;
     190  #ifdef VMS
     191      char *suffix;
     192  #endif
     193      size_t size;
     194      struct nameseq *chain;
     195      unsigned int n;
     196    };
     197  
     198  /* This function is called by 'ar_scan' to match one archive
     199     element against the pattern in STATE.  */
     200  
     201  static intmax_t
     202  ar_glob_match (int desc UNUSED, const char *mem, int truncated UNUSED,
     203                 long int hdrpos UNUSED, long int datapos UNUSED,
     204                 long int size UNUSED, intmax_t date UNUSED, int uid UNUSED,
     205                 int gid UNUSED, unsigned int mode UNUSED, const void *arg)
     206  {
     207    struct ar_glob_state *state = (struct ar_glob_state *)arg;
     208  
     209    if (fnmatch (state->pattern, mem, FNM_PATHNAME|FNM_PERIOD) == 0)
     210      {
     211        /* We have a match.  Add it to the chain.  */
     212        struct nameseq *new = xcalloc (state->size);
     213  #ifdef VMS
     214        if (state->suffix)
     215          new->name = strcache_add(
     216              concat(5, state->arname, "(", mem, state->suffix, ")"));
     217        else
     218  #endif
     219          new->name = strcache_add(concat(4, state->arname, "(", mem, ")"));
     220        new->next = state->chain;
     221        state->chain = new;
     222        ++state->n;
     223      }
     224  
     225    return 0;
     226  }
     227  
     228  /* Return nonzero if PATTERN contains any metacharacters.
     229     Metacharacters can be quoted with backslashes if QUOTE is nonzero.  */
     230  static int
     231  ar_glob_pattern_p (const char *pattern, int quote)
     232  {
     233    const char *p;
     234    int opened = 0;
     235  
     236    for (p = pattern; *p != '\0'; ++p)
     237      switch (*p)
     238        {
     239        case '?':
     240        case '*':
     241          return 1;
     242  
     243        case '\\':
     244          if (quote)
     245            ++p;
     246          break;
     247  
     248        case '[':
     249          opened = 1;
     250          break;
     251  
     252        case ']':
     253          if (opened)
     254            return 1;
     255          break;
     256        }
     257  
     258    return 0;
     259  }
     260  
     261  /* Glob for MEMBER_PATTERN in archive ARNAME.
     262     Return a malloc'd chain of matching elements (or nil if none).  */
     263  
     264  struct nameseq *
     265  ar_glob (const char *arname, const char *member_pattern, size_t size)
     266  {
     267    struct ar_glob_state state;
     268    struct nameseq *n;
     269    const char **names;
     270    unsigned int i;
     271  #ifdef VMS
     272    char *vms_member_pattern;
     273  #endif
     274    if (! ar_glob_pattern_p (member_pattern, 1))
     275      return 0;
     276  
     277    /* Scan the archive for matches.
     278       ar_glob_match will accumulate them in STATE.chain.  */
     279    state.arname = arname;
     280    state.pattern = member_pattern;
     281  #ifdef VMS
     282      {
     283        /* In a copy of the pattern, find the suffix, save it and  remove it from
     284           the pattern */
     285        char *lastdot;
     286        vms_member_pattern = xstrdup(member_pattern);
     287        lastdot = strrchr(vms_member_pattern, '.');
     288        state.suffix = lastdot;
     289        if (lastdot)
     290          {
     291            state.suffix = xstrdup(lastdot);
     292            *lastdot = 0;
     293          }
     294        state.pattern = vms_member_pattern;
     295      }
     296  #endif
     297    state.size = size;
     298    state.chain = 0;
     299    state.n = 0;
     300    ar_scan (arname, ar_glob_match, &state);
     301  
     302  #ifdef VMS
     303    /* Deallocate any duplicated string */
     304    free(vms_member_pattern);
     305    if (state.suffix)
     306      {
     307        free(state.suffix);
     308      }
     309  #endif
     310  
     311    if (state.chain == 0)
     312      return 0;
     313  
     314    /* Now put the names into a vector for sorting.  */
     315    names = alloca (state.n * sizeof (const char *));
     316    i = 0;
     317    for (n = state.chain; n != 0; n = n->next)
     318      names[i++] = n->name;
     319  
     320    /* Sort them alphabetically.  */
     321    /* MSVC erroneously warns without a cast here.  */
     322    qsort ((void *)names, i, sizeof (*names), alpha_compare);
     323  
     324    /* Put them back into the chain in the sorted order.  */
     325    i = 0;
     326    for (n = state.chain; n != 0; n = n->next)
     327      n->name = names[i++];
     328  
     329    return state.chain;
     330  }
     331  
     332  #endif  /* Not NO_ARCHIVES.  */