Ignore:
Timestamp:
Apr 14, 2020, 9:53:09 PM (5 years ago)
Author:
bird
Message:

kmk: Added quote* functions for GNU make and bourne shell style quoting/escaping. Also added q* and *file* versions of a bunch of others, to deal with quoted input and output in a flexible manner.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kmk/function.c

    r3317 r3319  
    57995799}
    58005800
     5801/* Helper for performer GNU make style quoting of one filename. */
     5802
     5803static char *helper_quote_make (char *o, const char *name, int is_dep, int is_tgt,
     5804                                int quote_trailing_slashes, const char *funcname)
     5805{
     5806  unsigned const map_flags = MAP_NUL
     5807                           | MAP_BLANK
     5808                           | MAP_NEWLINE
     5809                           | MAP_COMMENT
     5810                           | MAP_VARIABLE
     5811                           | MAP_SEMI
     5812                           | MAP_EQUALS
     5813                           | (is_dep ? MAP_PIPE :
     5814                              is_tgt ? MAP_COLON : 0);
     5815  const char *cur = name;
     5816  if (*cur)
     5817    {
     5818      unsigned long len_out = 0;
     5819      const char *prev = cur;
     5820      for (;;)
     5821        {
     5822          char ch = *cur;
     5823          unsigned int flags = stopchar_map[(unsigned int)ch] & map_flags;
     5824          if (!flags)
     5825            cur++; /* likely */
     5826          else
     5827            {
     5828              /* Flush pending output. */
     5829              if (prev != cur)
     5830                {
     5831                  o = variable_buffer_output (o, prev, cur - prev);
     5832                  len_out += cur  - prev;
     5833                }
     5834
     5835              if (flags & MAP_NUL)
     5836                break;
     5837
     5838              /* Dollar is quoted by duplicating the dollar: */
     5839              if (flags & MAP_VARIABLE)
     5840                {
     5841                  prev = cur++;
     5842                  o = variable_buffer_output (o, "$", 1);
     5843                }
     5844              /* The rest is quoted by '\': */
     5845              else
     5846                {
     5847                  size_t const max_slashes = cur - prev;
     5848                  size_t slashes = 0;
     5849                  while (slashes < max_slashes && cur[1 - slashes] == '\\')
     5850                    slashes++;
     5851                  if (slashes)
     5852                    {
     5853                      o  = variable_buffer_output (o, &cur[0 - slashes], slashes);
     5854                      len_out += slashes;
     5855                    }
     5856                  o  = variable_buffer_output (o, "\\", 1);
     5857                  prev = cur++;
     5858                }
     5859            }
     5860        }
     5861
     5862      /* Escape trailing slashes when needed. */
     5863      if (   o[-1] == '\\'
     5864          && quote_trailing_slashes)
     5865        {
     5866          size_t slashes = 1;
     5867          while (slashes < len_out && o[-1 - slashes] == '\\')
     5868            slashes++;
     5869          while (slashes-- > 0)
     5870            o  = variable_buffer_output (o, "\\", 1);
     5871        }
     5872    }
     5873  else
     5874      OS (message, 0, "%s: cannot quote empty string", funcname);
     5875  return o;
     5876}
     5877
     5878/* Helper for func_quote_make that checks if there are more arguments
     5879   that produces output or not. */
     5880
     5881static int func_quote_make_has_more_non_empty_args (char **argv)
     5882{
     5883  for (;;)
     5884    {
     5885      char *arg = *argv;
     5886      if (!arg)
     5887        return 0;
     5888      if (*arg)
     5889        return 1;
     5890      argv++;
     5891    }
     5892}
     5893
     5894/* Takes zero or more plain strings and escapes (quotes) spaces and other
     5895   problematic characters, GNU make style.
     5896
     5897   There is one slightly problematic aspect of using this, if the input ends
     5898   with backslashes whether or not they will be reduced or taken as-is depends
     5899   on whether they appear at the end of a line or not.  They are taken as-is
     5900   when at the end of a line, otherwise they'll be subject to unescaping
     5901   (unquoting) and reduced by half.
     5902
     5903   In addition, the quoting style differs for files on the left side and
     5904   right side of the recipe colon.  Colons aren't escaped are only escaped
     5905   on the left side (target), and the pipe character is only escaped on the
     5906   right side (deps).
     5907
     5908   For this reason there are four variants of this function. */
     5909
     5910static char *func_quote_make (char *o, char **argv, const char *funcname)
     5911{
     5912  int const is_dep = funcname[5] == '-' && funcname[6] == 'd';
     5913  int const is_tgt = funcname[5] == '-' && funcname[6] == 't';
     5914  int const quote_trailing_slashes = funcname[5] == '\0' || funcname[9] == '\0';
     5915  unsigned const map_flags = MAP_NUL
     5916                           | MAP_BLANK
     5917                           | MAP_NEWLINE
     5918                           | MAP_COMMENT
     5919                           | MAP_VARIABLE
     5920                           | MAP_SEMI
     5921                           | MAP_EQUALS
     5922                           | (is_dep ? MAP_PIPE :
     5923                              is_tgt ? MAP_COLON : 0);
     5924  char * const o_initial = o;
     5925  int i;
     5926
     5927  assert (    quote_trailing_slashes
     5928          == (!strcmp (funcname, "quote") || !strcmp (funcname, "quote-dep") || !strcmp (funcname, "quote-tgt")));
     5929  assert (is_dep == !strncmp (funcname, "quote-dep", sizeof ("quote-dep") - 1));
     5930  assert (is_tgt == !strncmp (funcname, "quote-tgt", sizeof ("quote-tgt") - 1));
     5931
     5932  for (i = 0; argv[i]; i++)
     5933    {
     5934      char *arg = argv[i];
     5935      if (*arg)
     5936        {
     5937          /* Add space separator. */
     5938          if (o != o_initial)
     5939            o = variable_buffer_output (o, " ", 1);
     5940
     5941          /* Output the quoted argument: */
     5942          if (quote_trailing_slashes)
     5943            o = helper_quote_make (o, arg, is_dep, is_tgt,
     5944                                   quote_trailing_slashes, funcname);
     5945          else
     5946            {
     5947              char *end = strchr (arg, '\0');
     5948              int qts = end != arg && end[-1] == '\\'
     5949                     && func_quote_make_has_more_non_empty_args (&argv[i + 1]);
     5950              o = helper_quote_make (o, arg, is_dep, is_tgt, qts, funcname);
     5951            }
     5952        }
     5953      else
     5954          OS (message, 0, "%s: cannot munge empty string", funcname);
     5955    }
     5956
     5957  return o;
     5958}
     5959
     5960/* Worker for func_quote_shell() for escaping a string that's inside
     5961   double quotes. */
     5962
     5963static char *func_escape_shell_in_dq (char *o, const char *arg, size_t len)
     5964{
     5965  const char *prev = arg;
     5966  while (len-- > 0)
     5967    {
     5968      char const ch = *arg;
     5969      switch (ch)
     5970        {
     5971          default:
     5972            arg++;
     5973            break;
     5974          case '!':
     5975          case '$':
     5976          case '`':
     5977          case '"':
     5978          case '\\':
     5979          case '\n':
     5980            if (prev != arg)
     5981              o = variable_buffer_output (o, prev, arg - prev);
     5982            o = variable_buffer_output (o, "\\", 1);
     5983            prev = arg;
     5984            arg++;
     5985            break;
     5986        }
     5987    }
     5988  if (prev != arg)
     5989    o = variable_buffer_output (o, prev, arg - prev);
     5990  return o;
     5991}
     5992
     5993/* quote-sh-dq */
     5994
     5995static char *func_quote_shell_dq (char *o, char **argv, const char *funcname UNUSED)
     5996{
     5997  return func_escape_shell_in_dq (o, argv[0], strlen (argv[0]));
     5998}
     5999
     6000
     6001/* Worker for func_quote_shell() for escaping a string that's inside
     6002   single quotes. */
     6003
     6004static char *func_escape_shell_in_sq (char *o, const char *arg, size_t len)
     6005{
     6006  while (len > 0)
     6007    {
     6008      char *sq = memchr (arg, '\'', len);
     6009      if (!sq)
     6010          return variable_buffer_output (o, arg, len);
     6011      if (sq != arg)
     6012        o = variable_buffer_output (o, arg, sq - arg);
     6013      o = variable_buffer_output (o, "'\\''", 4);
     6014
     6015      /* advance */
     6016      sq++;
     6017      len -= sq - arg;
     6018      arg = sq;
     6019    }
     6020  return o;
     6021}
     6022
     6023/* quote-sh-dq */
     6024
     6025static char *func_quote_shell_sq (char *o, char **argv, const char *funcname UNUSED)
     6026{
     6027  return func_escape_shell_in_sq (o, argv[0], strlen (argv[0]));
     6028}
     6029
     6030/* Output a shell argument with quoting as needed. */
     6031static char *helper_quote_shell (char *o, const char *arg, size_t len,
     6032                                 int leading_space)
     6033{
     6034  if (   memchr (arg, '$', len) != NULL
     6035      || memchr (arg, '*', len) != NULL
     6036      || memchr (arg, '?', len) != NULL
     6037      || memchr (arg, '[', len) != NULL)
     6038    {
     6039      if (leading_space)
     6040        o = variable_buffer_output (o, " '", 2);
     6041      else
     6042        o = variable_buffer_output (o, "'", 1);
     6043      o = func_escape_shell_in_sq (o, arg, len);
     6044      o = variable_buffer_output (o, "'", 1);
     6045    }
     6046  else if (   memchr (arg, ' ',  len) != NULL
     6047           || memchr (arg, '\t', len) != NULL
     6048           || memchr (arg, '\\', len) != NULL
     6049           || memchr (arg, '"',  len) != NULL
     6050           || memchr (arg, '`',  len) != NULL
     6051           || memchr (arg, '!',  len) != NULL
     6052           || memchr (arg, '|',  len) != NULL
     6053           || memchr (arg, '<',  len) != NULL
     6054           || memchr (arg, '>',  len) != NULL
     6055           || memchr (arg, '&',  len) != NULL
     6056           || memchr (arg, ';',  len) != NULL
     6057           || memchr (arg, '(',  len) != NULL
     6058           || memchr (arg, ')',  len) != NULL
     6059           || memchr (arg, '\n', len) != NULL)
     6060    {
     6061      if (leading_space)
     6062        o = variable_buffer_output (o, " \"", 2);
     6063      else
     6064        o = variable_buffer_output (o, "\"", 1);
     6065      o = func_escape_shell_in_dq (o, arg, len);
     6066      o = variable_buffer_output (o, "\"", 1);
     6067    }
     6068  else
     6069    {
     6070      if (leading_space)
     6071        o = variable_buffer_output (o, " ", 1);
     6072      o = variable_buffer_output (o, arg, len);
     6073    }
     6074  return o;
     6075}
     6076
     6077/* Takes zero or more plain strings and escapes/quotes spaces and other
     6078   problematic characters, bourne make style.
     6079
     6080   The quote-sh-dq and quote-sh-sq variants is for escaping strings that
     6081   going to be put into double quotes and single quotes respectively.
     6082
     6083   The normal quote-sh variant assumes it's free to do open and close
     6084   quotes as it pleases. */
     6085
     6086static char *func_quote_shell (char *o, char **argv, const char *funcname UNUSED)
     6087{
     6088  int i;
     6089  for (i = 0; argv[i]; i++)
     6090    o = helper_quote_shell (o, argv[i], strlen (argv[i]),
     6091                            i > 0 /*need_leading_space*/);
     6092  return o;
     6093}
     6094
     6095/* Unlinks CUR from *CHAINP and frees it, returning the next element. */
     6096
     6097static struct nameseq *
     6098helper_unlink_and_free_ns (struct nameseq *cur, struct nameseq *prev,
     6099                           struct nameseq **chainp)
     6100{
     6101  struct nameseq *freeit = cur; \
     6102    free ((char *)cur->name);
     6103  if (prev)
     6104    prev->next = cur = cur->next;
     6105  else
     6106    *chainp = cur = cur->next;
     6107  free_ns (freeit);
     6108  return cur;
     6109}
     6110
     6111/* Frees a chain returned by helper_parse_file_list. */
     6112
     6113static void free_ns_chain_no_strcache (struct nameseq *ns)
     6114{
     6115  while (ns != 0)
     6116    {
     6117      struct nameseq *t = ns;
     6118      free ((char *)ns->name);
     6119      ns = ns->next;
     6120      free_ns (t);
     6121    }
     6122}
     6123
     6124
     6125#define Q_RET_MASK              0x000f
     6126#define Q_RET_UNQUOTED          0x0000
     6127#define Q_RET_QUOTED            0x0001
     6128#define Q_RET_QUOTED_DEP        0x0002
     6129#define Q_RET_QUOTED_DEP_END    0x0003
     6130#define Q_RET_QUOTED_TGT        0x0004
     6131#define Q_RET_QUOTED_TGT_END    0x0005
     6132#define Q_RET_SHELL             0x0006
     6133#define Q_RET_SHELL_IN_DQ       0x0007
     6134#define Q_RET_SHELL_IN_SQ       0x0008
     6135
     6136#define Q_IN_MASK               0x0030
     6137#define Q_IN_QUOTED             0x0000
     6138#define Q_IN_UNQUOTED           0x0010
     6139#define Q_IN_QUOTED_DEP         0x0020  /** @todo needed? */
     6140#define Q_IN_QUOTED_TGT         0x0030  /** @todo needed? */
     6141#define Q_IN_SEP_COMMA          0x0040  /* for VMS hacks, file lists only */
     6142
     6143#define Q_SEP_MASK              0x0700
     6144#define Q_SEP_SHIFT             8
     6145#define Q_SEP_SPACE             0x0000
     6146#define Q_SEP_TAB               0x0100
     6147#define Q_SEP_NL                0x0200
     6148#define Q_SEP_NL_TAB            0x0300
     6149#define Q_SEP_COMMA             0x0400  /* for VMS, output only */
     6150
     6151#define Q_QDEFAULT              0x0000
     6152#ifndef VMS
     6153# define Q_QDEFAULT_VMS_TRICKS  Q_QDEFAULT
     6154#else /* VMS: Treat ',' as file separators in input, maybe output too. */
     6155# define Q_QDEFAULT_VMS_TRICKS  (Q_IN_SEP_COMMA | \
     6156    (!vms_comma_separator ? Q_QDEFAULT \
     6157     : (Q_QDEFAULT & ~Q_SEP_MASK) | Q_SEP_COMMA)
     6158#endif
     6159
     6160
     6161/* Decodes the optional style argument.  This is chiefly for the return
     6162   style, but can also pick the input and space styles (just because we can).  */
     6163
     6164static unsigned int helper_file_return_style (char *style, unsigned int intstyle)
     6165{
     6166  if (style != NULL)
     6167    {
     6168      for (;;)
     6169        {
     6170          /* Skip blanks: */
     6171          while (ISBLANK(*style))
     6172            style++;
     6173          if (*style != '\0')
     6174            {
     6175              /* Find the end of the current word: */
     6176              char * const start = style;
     6177              size_t len;
     6178              char ch;
     6179              while (!ISBLANK((ch = *style)) && ch != '\0')
     6180                style++;
     6181              len = style - start;
     6182
     6183              /* String "switch" to decode the word: */
     6184
     6185#define MATCH(a_str)  (len == sizeof (a_str) - 1 && memcmp (start, a_str, sizeof (a_str)) == 0)
     6186              /* return styles: */
     6187              if (MATCH ("quoted")              || MATCH ("q"))
     6188                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_QUOTED;
     6189              else if (MATCH ("unquoted")       || MATCH ("unq")        || MATCH ("u"))
     6190                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_UNQUOTED;
     6191              else if (MATCH ("quoted-dep")     || MATCH ("q-dep")      || MATCH ("q-d"))
     6192                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_QUOTED_DEP;
     6193              else if (MATCH ("quoted-dep-end") || MATCH ("q-dep-end")  || MATCH ("q-d-e"))
     6194                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_QUOTED_DEP_END;
     6195              else if (MATCH ("quoted-tgt")     || MATCH ("q-tgt")      || MATCH ("q-t"))
     6196                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_QUOTED_TGT;
     6197              else if (MATCH ("quoted-tgt-end") || MATCH ("q-tgt-end")  || MATCH ("q-t-e"))
     6198                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_QUOTED_TGT_END;
     6199              else if (MATCH ("shell")          || MATCH ("sh"))
     6200                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_SHELL;
     6201              else if (MATCH ("shell-in-dq")    || MATCH ("sh-i-d"))
     6202                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_SHELL_IN_DQ;
     6203              else if (MATCH ("shell-in-sq")    || MATCH ("sh-i-s"))
     6204                intstyle = (intstyle & ~Q_RET_MASK) | Q_RET_SHELL_IN_SQ;
     6205              /* input styles: */
     6206              else if (MATCH ("in-quoted")         || MATCH ("i-q"))
     6207                intstyle = (intstyle & ~Q_IN_MASK) | Q_IN_QUOTED;
     6208              else if (MATCH ("in-unquoted")       || MATCH ("i-unq")   || MATCH ("i-u"))
     6209                intstyle = (intstyle & ~Q_IN_MASK) | Q_IN_UNQUOTED;
     6210              else if (MATCH ("in-quoted-dep")     || MATCH ("i-q-dep") || MATCH ("i-q-d"))
     6211                intstyle = (intstyle & ~Q_IN_MASK) | Q_IN_QUOTED_DEP;
     6212              else if (MATCH ("in-quoted-tgt")     || MATCH ("i-q-tgt") || MATCH ("i-q-t"))
     6213                intstyle = (intstyle & ~Q_IN_MASK) | Q_IN_QUOTED_TGT;
     6214              else if (MATCH ("in-sep-comma")      || MATCH ("i-s-com") || MATCH ("i-s-c"))
     6215                intstyle = (intstyle & ~Q_SEP_MASK) | Q_IN_SEP_COMMA;
     6216              /* separator styles (output): */
     6217              else if (MATCH ("sep-space")  || MATCH ("s-space")  || MATCH ("s-s"))
     6218                intstyle = (intstyle & ~Q_SEP_MASK) | Q_SEP_SPACE;
     6219              else if (MATCH ("sep-tab")    || MATCH ("s-tab")    || MATCH ("s-t"))
     6220                intstyle = (intstyle & ~Q_SEP_MASK) | Q_SEP_TAB;
     6221              else if (MATCH ("sep-nl")     || MATCH ("s-nl")     || MATCH ("s-n"))
     6222                intstyle = (intstyle & ~Q_SEP_MASK) | Q_SEP_NL;
     6223              else if (MATCH ("sep-nl-tab") || MATCH ("s-nl-tab") || MATCH ("s-n-t"))
     6224                intstyle = (intstyle & ~Q_SEP_MASK) | Q_SEP_NL_TAB;
     6225              else if (MATCH ("sep-comma")  || MATCH ("s-comma")  || MATCH ("s-c"))
     6226                intstyle = (intstyle & ~Q_SEP_MASK) | Q_SEP_COMMA;
     6227              else
     6228                {
     6229                  char savedch = *style;
     6230                  *style = '\0';
     6231                  OS (error, reading_file, "Unknown file return style: %s", start);
     6232                  *style = savedch;
     6233                }
     6234#undef MATCH
     6235            }
     6236          else
     6237            break;
     6238        }
     6239    }
     6240  return intstyle;
     6241}
     6242
     6243/* Output (returns) a separator according to STYLE. */
     6244
     6245static char *helper_return_sep (char *o, unsigned int style)
     6246{
     6247  /* Note! Must match Q_SEP_MASK! */
     6248  static struct
     6249  {
     6250    const char *sep;
     6251    size_t len;
     6252  } const seps[8] =
     6253  {
     6254    { " ",    1 },
     6255    { "\t",   1 },
     6256    { "\n",   1 },
     6257    { "\n\t", 2 },
     6258    { ",",    1 },
     6259    { " ",    1 },
     6260    { " ",    1 },
     6261    { " ",    1 }
     6262  };
     6263
     6264  return variable_buffer_output(o,
     6265                                seps[(style & Q_SEP_MASK) >> Q_SEP_SHIFT].sep,
     6266                                seps[(style & Q_SEP_MASK) >> Q_SEP_SHIFT].len);
     6267}
     6268
     6269/* Outputs (returns) the given file. */
     6270
     6271static char *helper_return_file_len (char *o, const char *file, size_t len,
     6272                                     unsigned int style, int is_last)
     6273{
     6274  assert (file[len] == '\0');
     6275  switch (style & Q_RET_MASK)
     6276    {
     6277      case Q_RET_UNQUOTED:
     6278        o = variable_buffer_output (o, file, len);
     6279        break;
     6280      case Q_RET_QUOTED:
     6281        o = helper_quote_make (o, file, 0 /*is_dep*/, 0 /*is_tgt*/,
     6282                               !is_last /*quote_trailing_slashes*/, NULL);
     6283        break;
     6284      case Q_RET_QUOTED_DEP:
     6285        o = helper_quote_make (o, file, 1 /*is_dep*/, 0 /*is_tgt*/,
     6286                               !is_last /*quote_trailing_slashes*/, NULL);
     6287        break;
     6288      case Q_RET_QUOTED_DEP_END:
     6289        o = helper_quote_make (o, file, 1 /*is_dep*/, 0 /*is_tgt*/,
     6290                               0 /*quote_trailing_slashes*/, NULL);
     6291        break;
     6292      case Q_RET_QUOTED_TGT:
     6293        o = helper_quote_make (o, file, 0 /*is_dep*/, 1 /*is_tgt*/,
     6294                               !is_last /*quote_trailing_slashes*/, NULL);
     6295        break;
     6296      case Q_RET_QUOTED_TGT_END:
     6297        o = helper_quote_make (o, file, 0 /*is_dep*/, 1 /*is_tgt*/,
     6298                               0 /*quote_trailing_slashes*/, NULL);
     6299        break;
     6300      case Q_RET_SHELL:
     6301        o = helper_quote_shell (o, file, len, 0 /*need_leading_space*/);
     6302        break;
     6303      case Q_RET_SHELL_IN_DQ:
     6304        o = func_escape_shell_in_dq (o, file, len);
     6305        break;
     6306      case Q_RET_SHELL_IN_SQ:
     6307        o = func_escape_shell_in_sq (o, file, len);
     6308        break;
     6309      default:
     6310        assert (0);
     6311    }
     6312
     6313  /* Add separator space if not last. */
     6314  if (!is_last)
     6315    o = helper_return_sep (o, style);
     6316  return o;
     6317}
     6318
     6319/* Outputs (returns) the given file. */
     6320
     6321static char *helper_return_file (char *o, const char *file, unsigned int style, int is_last)
     6322{
     6323  return helper_return_file_len (o,file, strlen (file), style, is_last);
     6324}
     6325
     6326/* Outputs (returns) the given CHAIN and frees it. */
     6327
     6328static char *helper_return_and_free_chain (char *o, struct nameseq *chain, unsigned int style)
     6329{
     6330  struct nameseq *cur;
     6331  for (cur = chain; cur; cur = cur->next)
     6332    o = helper_return_file (o, cur->name, style, cur->next == NULL);
     6333  free_ns_chain_no_strcache (chain);
     6334  return o;
     6335}
     6336
     6337
     6338/* Helper for helper_parse_file_list that globs a name sequence. */
     6339static struct nameseq *
     6340helper_glob_chain (struct nameseq *chain)
     6341{
     6342  struct nameseq *prev = NULL;
     6343  struct nameseq *cur = chain;
     6344  glob_t gl;
     6345  dir_setup_glob (&gl);
     6346
     6347  /** @todo XXX: !NO_ARCHIVES  */
     6348  while (cur)
     6349    {
     6350      switch (glob (cur->name, GLOB_NOSORT | GLOB_ALTDIRFUNC, NULL, &gl))
     6351        {
     6352        case 0: /* Replace CUR with the names found. */
     6353          {
     6354            struct nameseq *subchain = NULL;
     6355            struct nameseq **ppnext = &subchain;
     6356            const char ** const names = (const char **)gl.gl_pathv;
     6357            size_t const num_names = gl.gl_pathc;
     6358            size_t idx;
     6359
     6360            cur = helper_unlink_and_free_ns (cur, prev, &chain);
     6361
     6362            for (idx = 0; idx < num_names; idx++)
     6363              {
     6364#ifndef CONFIG_WITH_ALLOC_CACHES
     6365                struct nameseq *newp = xcalloc (sizeof (*newp));
     6366#else
     6367                struct nameseq *newp = alloccache_calloc (&nameseq_cache);
     6368#endif
     6369                newp->name = xstrdup (names[idx]);
     6370                newp->next = NULL;
     6371                *ppnext = newp;
     6372                ppnext = &newp->next;
     6373              }
     6374
     6375            if (subchain) /* parnaoia */
     6376              {
     6377                *ppnext = cur;
     6378                if (prev)
     6379                  prev->next = subchain;
     6380                else
     6381                  chain = subchain;
     6382              }
     6383            break;
     6384          }
     6385
     6386        case GLOB_NOMATCH: /* doesn't exist, remove */
     6387          cur = helper_unlink_and_free_ns (cur, prev, &chain);
     6388          break;
     6389
     6390        default: /* Keep it. */
     6391          prev = cur;
     6392          cur = cur->next;
     6393          break;
     6394
     6395        case GLOB_NOSPACE:
     6396          OUT_OF_MEM();
     6397        }
     6398      globfree (&gl);
     6399    }
     6400  return chain;
     6401}
     6402
     6403/* Parses a file/word list according to STYLE and returns a name list. */
     6404
     6405static struct nameseq *
     6406helper_parse_file_list (char *filelist, unsigned int style, int glob)
     6407{
     6408  if (filelist && *filelist != '\0')
     6409    switch (style & (Q_IN_MASK | Q_IN_SEP_COMMA))
     6410      {
     6411        case Q_IN_QUOTED:
     6412        case Q_IN_QUOTED_DEP: /** @todo ?? */
     6413        case Q_IN_QUOTED_TGT: /** @todo ?? */
     6414          return PARSE_FILE_SEQ(&filelist, struct nameseq, MAP_NUL, NULL,
     6415                                !glob
     6416                                ? PARSEFS_NOGLOB | PARSEFS_NOSTRIP | PARSEFS_NOCACHE
     6417                                : PARSEFS_NOSTRIP | PARSEFS_NOCACHE | PARSEFS_EXISTS);
     6418
     6419        case Q_IN_UNQUOTED:
     6420         {
     6421           struct nameseq *chain = NULL;
     6422           struct nameseq **ppnext = &chain;
     6423           const char *it = filelist;
     6424           const char *cur;
     6425           unsigned int curlen;
     6426           while ((cur = find_next_token (&it, &curlen)) != NULL)
     6427             {
     6428#ifndef CONFIG_WITH_ALLOC_CACHES
     6429                struct nameseq *newp = xcalloc (sizeof (*newp));
     6430#else
     6431                struct nameseq *newp = alloccache_calloc (&nameseq_cache);
     6432#endif
     6433                newp->name = xstrndup (cur, curlen);
     6434                newp->next = NULL;
     6435                *ppnext = newp;
     6436                ppnext = &newp->next;
     6437             }
     6438           if (!glob)
     6439             return chain;
     6440           return helper_glob_chain (chain);
     6441         }
     6442
     6443        /* Following works recursively. Mainly for VMS. */
     6444        case Q_IN_SEP_COMMA | Q_IN_UNQUOTED:
     6445        case Q_IN_SEP_COMMA | Q_IN_QUOTED:
     6446        case Q_IN_SEP_COMMA | Q_IN_QUOTED_DEP: /** @todo ?? */
     6447        case Q_IN_SEP_COMMA | Q_IN_QUOTED_TGT: /** @todo ?? */
     6448          {
     6449            size_t len = strlen (filelist);
     6450            char *comma = (char *)memchr (filelist, ',', len);
     6451            struct nameseq *chain;
     6452            if (!comma)
     6453              chain = helper_parse_file_list (filelist, style & ~Q_IN_SEP_COMMA, 0);
     6454            else
     6455              {
     6456                char *copy;
     6457                char *start;
     6458                start = copy = xmalloc (len + 1);
     6459                memcpy (copy, filelist, len + 1);
     6460                comma = copy + (comma - filelist);
     6461                do
     6462                  {
     6463                    *comma = ' ';
     6464                    len -= comma - start - 1;
     6465                    if (len)
     6466                      {
     6467                        start = comma + 1;
     6468                        comma = (char *)memchr (start, ',', len);
     6469                      }
     6470                    else
     6471                      break;
     6472                  }
     6473                while (comma != NULL);
     6474
     6475                chain = helper_parse_file_list (filelist, style & ~Q_IN_SEP_COMMA, 0);
     6476
     6477                free (copy);
     6478              }
     6479            return chain;
     6480          }
     6481
     6482        default:
     6483          assert (0);
     6484          return NULL;
     6485      }
     6486  return NULL;
     6487}
     6488
     6489/* $(firstfile file1 file2 ... fileN) - same as $(firstfile ), except for files
     6490   rather than word tokens.  See func_firstword().  */
     6491
     6492static char *func_firstfile (char *o, char **argv, const char *funcname UNUSED)
     6493{
     6494  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     6495  char *line = argv[0];
     6496  if (line && *line != '\0')
     6497    {
     6498      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6499      if (chain)
     6500        {
     6501          o = helper_return_file (o, chain->name, style, 1);
     6502          free_ns_chain_no_strcache (chain);
     6503        }
     6504    }
     6505  return o;
     6506}
     6507
     6508/* $(lastfile file1 file2 ... fileN) - same as $(lastfile ), except for files
     6509   rather than word tokens.  See func_lastword(). */
     6510
     6511static char *func_lastfile (char *o, char **argv, const char *funcname UNUSED)
     6512{
     6513  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     6514  char *line = argv[0];
     6515  if (line && *line != '\0')
     6516    {
     6517      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6518      if (chain)
     6519        {
     6520          struct nameseq *last = chain;
     6521          while (last->next)
     6522            last = last->next;
     6523          o = helper_return_file (o, last->name, style, 1);
     6524          free_ns_chain_no_strcache (chain);
     6525        }
     6526    }
     6527  return o;
     6528}
     6529
     6530/* $(filelist start, end, file1..fileN [, style]) - same as $(wordlist),
     6531   except for files rather than word tokens.  See func_wordlist(). */
     6532
     6533static char *func_filelist (char *o, char **argv, const char *funcname UNUSED)
     6534{
     6535  unsigned int const style = helper_file_return_style (argv[3], Q_QDEFAULT);
     6536  int start;
     6537  int count;
     6538
     6539  /* Check the arguments.  */
     6540  check_numeric (argv[0],
     6541                 _("non-numeric first argument to 'filelist' function"));
     6542  check_numeric (argv[1],
     6543                 _("non-numeric second argument to 'filelist' function"));
     6544
     6545  start = atoi (argv[0]);
     6546  if (start < 1)
     6547    ON (fatal, *expanding_var,
     6548        "invalid first argument to 'filelist' function: '%d'", start);
     6549  start--; /* make zero based */
     6550
     6551  count = atoi (argv[1]) - start;
     6552
     6553  if (count > 0)
     6554    {
     6555      char *line = argv[2];
     6556      struct nameseq *cur;
     6557      struct nameseq *chain;
     6558      chain = helper_parse_file_list (line, style, 0);
     6559
     6560      /* Find the beginning of the "start"th word (1-based).  */
     6561      for (cur = chain; cur && start > 1; cur = cur->next)
     6562        start--;
     6563
     6564      /* Output the requested count */
     6565      while (cur && count-- > 0)
     6566        o = helper_return_file (o, cur->name, style, count > 0 && cur->next);
     6567
     6568      free_ns_chain_no_strcache (chain);
     6569    }
     6570
     6571  return o;
     6572}
     6573
     6574/* $(countfiles file1 file2 ... fileN[,style]) - same as $(words ), except for
     6575   files rather than word tokens.  See func_words(). */
     6576
     6577static char *func_countfiles (char *o, char **argv, const char *funcname UNUSED)
     6578{
     6579  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT); /* simpler */
     6580  char retval[32];
     6581  unsigned int files = 0;
     6582  char *line = argv[0];
     6583  if (line && *line != '\0')
     6584    {
     6585      struct nameseq *cur;
     6586      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6587      for (cur = chain; cur; cur = cur->next)
     6588        files++;
     6589      free_ns_chain_no_strcache (chain);
     6590    }
     6591
     6592  return variable_buffer_output (o, retval, sprintf (retval, "%u", files));
     6593}
     6594
     6595/* Helper that sets the variable value. */
     6596
     6597static void
     6598helper_set_var_value (struct variable *var, const char *value, size_t len)
     6599{
     6600#ifndef CONFIG_WITH_VALUE_LENGTH
     6601  free (var->value);
     6602  var->value = xstrndup (value, len);
     6603#else
     6604  if (len >= var->value_alloc_len)
     6605    {
     6606# ifdef CONFIG_WITH_RDONLY_VARIABLE_VALUE
     6607      if (var->rdonly_val)
     6608        var->rdonly_val = 0;
     6609      else
     6610# endif
     6611        free (var->value);
     6612      var->value_alloc_len = VAR_ALIGN_VALUE_ALLOC (len + 1);
     6613      var->value = xmalloc (var->value_alloc_len);
     6614    }
     6615  memcpy (var->value, value, len);
     6616  var->value[len] = '\0';
     6617  var->value_length = len;
     6618  VARIABLE_CHANGED (var);
     6619#endif
     6620}
     6621
     6622/* $(foreachfile var, filelist, body [, style]) - same as $(foreach ), except
     6623   for file rather than word tokens and flexible variable value encoding.
     6624   See also func_foreach(). */
     6625
     6626static char *
     6627func_foreachfile (char *o, char **argv, const char *funcname UNUSED)
     6628{
     6629  /* expand only the first two.  */
     6630  char *varname = expand_argument (argv[0], NULL);
     6631  char *list = expand_argument (argv[1], NULL);
     6632  const char *body = argv[2];
     6633#ifdef CONFIG_WITH_VALUE_LENGTH
     6634  long body_len = strlen (body);
     6635#endif
     6636
     6637  unsigned int const style = helper_file_return_style (argv[3], Q_QDEFAULT);
     6638  struct nameseq *chain = helper_parse_file_list (list, style, 0);
     6639  struct nameseq *cur;
     6640
     6641  /* Clean up the variable name by removing whitespace.  */
     6642  struct variable *var;
     6643  char *vp = next_token (varname);
     6644  char *vp_end = end_of_token (vp);
     6645  vp_end[0] = '\0';
     6646
     6647  push_new_variable_scope ();
     6648  var = define_variable (vp, vp_end - vp, "", o_automatic, 0);
     6649
     6650  /* Don't need the list any more. */
     6651  free (list);
     6652  list = NULL;
     6653
     6654  /* Loop through the chain. */
     6655  for (cur = chain; cur; cur = cur->next)
     6656    {
     6657      /* Update the variable value: */
     6658      unsigned int const len = strlen (cur->name);
     6659      switch (style & Q_RET_MASK)
     6660        {
     6661        case Q_RET_UNQUOTED:
     6662          helper_set_var_value (var, cur->name, len);
     6663          break;
     6664        default:
     6665          { /* Use the output buffer as temporary storage. */
     6666            size_t const saved_off = o - variable_buffer;
     6667            size_t quoted_len;
     6668            char *quoted;
     6669            o = helper_return_file_len (o, cur->name, len, style, 1 /*is_last*/);
     6670            quoted = &variable_buffer[saved_off];
     6671            quoted_len = o - quoted;
     6672            helper_set_var_value (var, quoted, quoted_len);
     6673            o = quoted;
     6674            break;
     6675          }
     6676        }
     6677
     6678      /* Expand the body: */
     6679#ifndef CONFIG_WITH_VALUE_LENGTH
     6680      {
     6681        char *result = allocated_variable_expand (body);
     6682        o = variable_buffer_output (o, result, strlen (result));
     6683        free (result);
     6684      }
     6685#else
     6686      variable_expand_string_2 (o, body, body_len, &o);
     6687#endif
     6688
     6689      /* Add separator: */
     6690      if (cur->next)
     6691        o = helper_return_sep (o, style);
     6692    }
     6693
     6694  pop_variable_scope ();
     6695  free (varname);
     6696
     6697  return o;
     6698}
     6699
     6700/* $(sortfiles file1 ... fileN [,style]) and
     6701   $(rsortfiles file1 ... fileN [,style]) - same to $(sort ) and $(rsort ),
     6702   except for files rather than word tokens.  See func_sort(). */
     6703
     6704static char *func_sortfiles (char *o, char **argv, const char *funcname UNUSED)
     6705{
     6706  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     6707  char *line = argv[0];
     6708  if (line && *line != '\0')
     6709    {
     6710      unsigned int num_files = 0;
     6711      struct nameseq *cur;
     6712      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6713      for (cur = chain; cur; cur = cur->next)
     6714        num_files++;
     6715      if (num_files > 0)
     6716        {
     6717          char *prev_file;
     6718          char **files = (char **)xmalloc (num_files * sizeof (char *));
     6719          unsigned int idx = 0;
     6720          for (cur = chain; cur; cur = cur->next)
     6721            files[idx++] = (char *)cur->name;
     6722
     6723          qsort (files, num_files, sizeof (char *), alpha_compare);
     6724
     6725          prev_file = NULL;
     6726          if (funcname[0] == 'r')
     6727            {
     6728              idx = num_files;
     6729              while (idx-- > 0)
     6730                if (prev_file == NULL || strcmp (files[idx], prev_file) != 0)
     6731                  {
     6732                    prev_file = files[idx];
     6733                    o = helper_return_file (o, files[idx], style, idx == 0);
     6734                  }
     6735            }
     6736          else
     6737            for (idx = 0; idx < num_files; idx++)
     6738              if (prev_file == NULL || strcmp (files[idx], prev_file) != 0)
     6739                {
     6740                  prev_file = files[idx];
     6741                  o = helper_return_file(o, files[idx], style,
     6742                                         idx + 1 == num_files);
     6743                }
     6744
     6745          free (files);
     6746        }
     6747      free_ns_chain_no_strcache (chain);
     6748    }
     6749
     6750  return o;
     6751}
     6752
     6753/* Helper for determining whether the given path is absolute or not. */
     6754
     6755static int helper_is_abs (const char *path)
     6756{
     6757#ifdef HAVE_DOS_PATHS
     6758  return path[0] == '/'
     6759      || path[0] == '\\'
     6760      || (isalpha(path[0]) && path[1] == ':');
     6761#else
     6762  (void)len;
     6763  return path[0] == '/';
     6764#endif
     6765}
     6766
     6767/* Worker for func_q_abspath and func_q_abspath_ex. */
     6768
     6769static char *worker_abspath (char *o, char *line, const char *cwd,
     6770                             size_t cwd_len, unsigned int style)
     6771{
     6772  if (line && *line != '\0')
     6773    {
     6774      PATH_VAR (outbuf);
     6775      int doneany = 0;
     6776      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6777
     6778      /* Special case: single path, no cwd - no is_last path trouble */
     6779      if (chain && !chain->next && !cwd)
     6780        {
     6781          if (abspath (chain->name, outbuf))
     6782            o = helper_return_file(o, outbuf, style, 1);
     6783          free_ns_chain_no_strcache (chain);
     6784        }
     6785      else if (chain)
     6786        {
     6787          /* Pass one: replace names by absolute names */
     6788          struct nameseq *prev = NULL;
     6789          struct nameseq *cur = chain;
     6790          while (cur)
     6791            {
     6792              /* If relative path and we've got cwd, join cwd and it. */
     6793              if (cwd && !helper_is_abs (cur->name))
     6794                {
     6795                  size_t len_name = strlen (cur->name);
     6796                  char *name = xrealloc ((char *)cur->name, cwd_len + 1 + len_name + 1);
     6797                  memmove (&name[cwd_len + 1], &name[0], len_name);
     6798                  memcpy (name, cwd, cwd_len);
     6799                  name[cwd_len] = '/';
     6800                  name[cwd_len + 1 + len_name] = '\0';
     6801                }
     6802
     6803              if (abspath (cur->name, outbuf))
     6804                {
     6805                  free ((char *)cur->name);
     6806                  cur->name = xstrdup (outbuf);
     6807                  prev = cur;
     6808                  cur->next;
     6809                }
     6810              else /* remove it */
     6811                cur = helper_unlink_and_free_ns (cur, prev, &chain);
     6812            }
     6813
     6814          /* Pass two: output */
     6815          o = helper_return_and_free_chain (o, chain, style);
     6816        }
     6817    }
     6818  return o;
     6819}
     6820
     6821/* $(qabspath file1 file2 ... fileN [, style]) - same to $(abspath ), except
     6822   for files rather than word tokens.  See func_abspath(). */
     6823
     6824static char *func_q_abspath (char *o, char **argv, const char *funcname UNUSED)
     6825{
     6826  return worker_abspath (o, argv[0], NULL, 0,
     6827                         helper_file_return_style (argv[1], Q_QDEFAULT));
     6828}
     6829
     6830# ifdef CONFIG_WITH_ABSPATHEX
     6831/* $(qabspathex file1 file2 ... fileN [,cwd [, style]]) - same to $(abspathex ),
     6832   except for files rather than word tokens.  See func_abspath_ex(). */
     6833
     6834static char *
     6835func_q_abspathex (char *o, char **argv, const char *funcname UNUSED)
     6836{
     6837  char *cwd = argv[1];
     6838  char *style = cwd ? argv[2] : NULL;
     6839
     6840  /* cwd needs leading spaces chopped and may be optional,
     6841     in which case we're exactly like $(abspath ). */
     6842  if (cwd)
     6843    {
     6844      while (ISBLANK (*cwd))
     6845        cwd++;
     6846      if (*cwd)
     6847        cwd = NULL;
     6848    }
     6849
     6850  return worker_abspath (o, argv[0], cwd, cwd ? strlen (cwd) : 0,
     6851                         helper_file_return_style (style, Q_QDEFAULT));
     6852}
     6853# endif
     6854
     6855/* $(qaddprefix prefix, file1 ... fileN [, style]) and
     6856   $(qaddsuffix prefix, file1 ... fileN [, style]) - same to $(addprefix )
     6857   and $(addsuffix ) except for files rather than word tokens.
     6858   The suffix/prefix is unquoted on input and subjected to the same quoting
     6859   styling as the file names.
     6860   See func_addsuffix_addprefix(). */
     6861
     6862static char *func_q_addsuffix_addprefix (char *o, char **argv, const char *funcname UNUSED)
     6863{
     6864  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     6865  const char * const fix = argv[0];
     6866  size_t const fixlen = strlen (fix);
     6867  char *line = argv[0];
     6868  if (line && *line != '\0')
     6869    {
     6870      size_t tmpsize = (fixlen + 512) & ~(size_t)63;
     6871      char *tmp = (char *)xmalloc (tmpsize);
     6872      struct nameseq *cur;
     6873      struct nameseq *chain = helper_parse_file_list (line, style, 0);
     6874
     6875      if (funcname[4] == 'p')
     6876        {
     6877          memcpy (tmp, fix, fixlen);
     6878          for (cur = chain; cur; cur = cur->next)
     6879            {
     6880              size_t curlen = strlen (cur->name);
     6881              if (fixlen + curlen + 1 <= tmpsize)
     6882                { /* likely */ }
     6883              else
     6884                {
     6885                  tmpsize = (fixlen + curlen + 63) & ~(size_t)63;
     6886                  tmp = (char *)xrealloc (tmp, tmpsize);
     6887                }
     6888              memcpy (&tmp[fixlen], cur->name, curlen + 1);
     6889              o = helper_return_file_len (o, tmp, fixlen + curlen,
     6890                                          style, cur->next == NULL);
     6891            }
     6892        }
     6893      else
     6894        for (cur = chain; cur; cur = cur->next)
     6895          {
     6896            size_t curlen = strlen (cur->name);
     6897            if (fixlen + curlen + 1 <= tmpsize)
     6898              { /* likely */ }
     6899            else
     6900              {
     6901                tmpsize = (fixlen + curlen + 63) & ~(size_t)63;
     6902                tmp = (char *)xrealloc (tmp, tmpsize);
     6903              }
     6904            memcpy (tmp, cur->name, curlen);
     6905            memcpy (&tmp[curlen], fix, fixlen + 1);
     6906
     6907            o = helper_return_file_len (o, tmp, fixlen + curlen,
     6908                                        style, cur->next == NULL);
     6909          }
     6910      free_ns_chain_no_strcache (chain);
     6911    }
     6912  return o;
     6913}
     6914
     6915/* $(qbasename path1 .. pathN[, style]) and $(qdir path1 .. pathN[, style])
     6916   - same to $(basename ) and $(dir ), except for files rather than word tokens.
     6917   See func_basename_dir(). */
     6918
     6919static char *
     6920func_q_basename_dir (char *o, char **argv, const char *funcname)
     6921{
     6922  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT_VMS_TRICKS);
     6923  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     6924  struct nameseq *cur;
     6925
     6926  int const is_basename = funcname[1] == 'b';
     6927  int const is_dir = !is_basename;
     6928  int const stop = MAP_DIRSEP | (is_basename ? MAP_DOT : 0) | MAP_NUL;
     6929
     6930  for (cur = chain; cur; cur = cur->next)
     6931    {
     6932      int const is_last = cur->next == NULL;
     6933      const char * const path = cur->name;
     6934      const char * const end = strchr (path, '\0');
     6935
     6936      /* Locate the last dot or path separator (P): */
     6937      const char *p = path != end ? end - 1 : end;
     6938      while (p >= path && !STOP_SET (*p, stop))
     6939        --p;
     6940
     6941      /* Do the outputting: */
     6942      if (p >= path && (is_dir))
     6943        o = helper_return_file_len (o, path, ++p - path, style, is_last);
     6944      else if (p >= path && *p == '.')
     6945        o = helper_return_file_len (o, path, p - path, style, is_last);
     6946#ifdef HAVE_DOS_PATHS
     6947      /* Handle the "d:foobar" case */
     6948      else if (path[0] && path[1] == ':' && is_dir)
     6949        o = helper_return_file_len (o, path, 2, style, is_last);
     6950#endif
     6951      else if (is_dir)
     6952#ifdef VMS
     6953        {
     6954          extern int vms_report_unix_paths;
     6955          o = helper_return_file_len (o, vms_report_unix_paths ? "./" : "[]",
     6956                                      2, style, is_last);
     6957        }
     6958#else
     6959# ifndef _AMIGA
     6960      o = helper_return_file_len (o, "./", 2, style, is_last);
     6961# else
     6962      ; /* Just a nop...  */
     6963# endif /* AMIGA */
     6964#endif /* !VMS */
     6965      else
     6966        /* The entire name is the basename.  */
     6967        o = helper_return_file_len (o, path, end - path, style, is_last);
     6968    }
     6969
     6970  free_ns_chain_no_strcache (chain);
     6971  return o;
     6972}
     6973
     6974/* $(qnotdir path1 ... pathN[, style]) - same as $(notdir ), except for
     6975   files rather than word tokens.  See func_notdir_suffix(). */
     6976
     6977static char *
     6978func_q_notdir (char *o, char **argv, const char *funcname)
     6979{
     6980  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT_VMS_TRICKS);
     6981  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     6982  struct nameseq *cur;
     6983  int const stop = MAP_DIRSEP;
     6984
     6985  for (cur = chain; cur; cur = cur->next)
     6986    {
     6987      int const is_last = cur->next == NULL;
     6988      const char * const path = cur->name;
     6989      const char * const end = strchr(path, '\0');
     6990
     6991      /* Locate the last dot or path separator (P): */
     6992      const char *p = path != end ? end - 1 : end;
     6993      while (p >= path && ! STOP_SET (*p, stop))
     6994        --p;
     6995
     6996      if ((uintptr_t)p >= (uintptr_t)path)
     6997          o = helper_return_file_len (o, p + 1, end - p - 1, style, is_last);
     6998#ifdef HAVE_DOS_PATHS
     6999      else if (path[0] && path[1] == ':')  /* "d:foo/bar" -> "foo/bar"  */
     7000        o = helper_return_file_len (o, path + 2, end - path - 2, style, is_last);
     7001#endif
     7002      else
     7003        o = helper_return_file_len (o, path, end - path, style, is_last);
     7004    }
     7005
     7006  free_ns_chain_no_strcache (chain);
     7007  return o;
     7008}
     7009
     7010/* $(qsuffix path1 ... pathN[, style]) - same as $(suffix ), except for
     7011   files rather than word tokens.  See func_notdir_suffix(). */
     7012
     7013static char *
     7014func_q_suffix (char *o, char **argv, const char *funcname)
     7015{
     7016  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT_VMS_TRICKS);
     7017  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     7018  struct nameseq *prev;
     7019  struct nameseq *cur;
     7020  int const stop = MAP_DIRSEP | MAP_DOT;
     7021
     7022  /* For suffixes we do a pre-pass that removes elements without suffixes.
     7023     This simplifies the handling of end-quoting. */
     7024  prev = NULL;
     7025  cur = chain;
     7026  while (cur)
     7027    {
     7028      const char * const path = cur->name;
     7029      if (strchr (path, '.') != NULL)
     7030        {
     7031          const char * const end = strchr (path, '\0');
     7032          const char *p = end - 1;
     7033          while ((uintptr_t)p >= (uintptr_t)path && ! STOP_SET (*p, stop))
     7034            --p;
     7035          if ((uintptr_t)p >= (uintptr_t)path && *p == '.')
     7036            {
     7037              if (p != path)
     7038                  memmove ((char *)path, p, end - p + 1);
     7039              prev = cur;
     7040              cur = cur->next;
     7041            }
     7042          else /* remove it */
     7043            cur = helper_unlink_and_free_ns (cur, prev, &chain);
     7044        }
     7045      else /* remove it */
     7046        cur = helper_unlink_and_free_ns (cur, prev, &chain);
     7047    }
     7048
     7049  /* Output pass: */
     7050  return helper_return_and_free_chain (o, chain, style);
     7051}
     7052
     7053# ifdef CONFIG_WITH_ROOT_FUNC
     7054/*
     7055 $(qroot path...pathN [,style]) - same as $(root ), except files rather
     7056 than space delimited word tokens.  See func_root().
     7057
     7058 This is mainly for dealing with drive letters and UNC paths on Windows
     7059 and OS/2.
     7060 */
     7061static char *
     7062func_q_root (char *o, char **argv, const char *funcname UNUSED)
     7063{
     7064  unsigned int const style = helper_file_return_style (argv[0] ? argv[1] : NULL, Q_QDEFAULT);
     7065  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     7066  struct nameseq *prev;
     7067  struct nameseq *cur;
     7068
     7069  /* First pass: Strip non-root components and remove rootless elements. */
     7070  prev = NULL;
     7071  cur = chain;
     7072  while (cur)
     7073    {
     7074      const char *path = cur->name;
     7075      const char *end  = NULL;
     7076      char ch;
     7077
     7078# ifdef HAVE_DOS_PATHS
     7079      if (isalpha(path[0]) && path[1] == ':')
     7080          end = path + 2;
     7081      else if (   IS_PATHSEP(path[0])
     7082               && IS_PATHSEP(path[1])
     7083               && !IS_PATHSEP(path[2]) && path[2]
     7084               && path[3])
     7085        {
     7086          /* Min recognized UNC: "//./" - find the next slash
     7087             Typical root: "//srv/shr/" */
     7088          /* XXX: Check if //./ needs special handling. */
     7089          end = path + 3;
     7090          while ((ch = *end) != '\0' && !IS_PATHSEP(ch))
     7091            end++;
     7092
     7093          if (IS_PATHSEP(ch) && !IS_PATHSEP(end[1]))
     7094            {
     7095              end++;
     7096              while ((ch = *end) != '\0' && !IS_PATHSEP(ch))
     7097                end++;
     7098            }
     7099          else
     7100            end = NULL; /* invalid */
     7101        }
     7102      else if (IS_PATHSEP(*end))
     7103        end = path + 1;
     7104      else
     7105        end = NULL;
     7106
     7107# elif defined (VMS) || defined (AMGIA)
     7108      /* XXX: VMS and AMGIA */
     7109      OS (fatal, NILF, _("$(%s ) is not implemented on this platform"), funcname);
     7110# else
     7111      if (IS_PATHSEP(*path))
     7112        end = path + 1;
     7113# endif
     7114      if (end != NULL)
     7115        {
     7116          /* Include all subsequent path separators. */
     7117
     7118          while ((ch = *end) != '\0' && IS_PATHSEP(ch))
     7119            end++;
     7120          *(char *)end = '\0';
     7121
     7122          prev = cur;
     7123          cur = cur->next;
     7124        }
     7125      else
     7126        cur = helper_unlink_and_free_ns(cur, prev, &chain);
     7127    }
     7128
     7129  /* Second pass: Output */
     7130  return helper_return_and_free_chain (o, chain, style);
     7131}
     7132
     7133/*
     7134 $(qnotroot path1 .. pathN [, style]) - same as $(notroot ), except files
     7135 rather than space delimited word tokens.  See func_notroot().
     7136
     7137 This is mainly for dealing with drive letters and UNC paths on Windows
     7138 and OS/2.
     7139 */
     7140static char *
     7141func_q_notroot (char *o, char **argv, const char *funcname UNUSED)
     7142{
     7143  unsigned int const style = helper_file_return_style (argv[0] ? argv[1] : NULL, Q_QDEFAULT);
     7144  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     7145  struct nameseq *cur;
     7146
     7147  for (cur = chain; cur; cur = cur->next)
     7148    {
     7149      const char *start = cur->name;
     7150      char ch;
     7151
     7152# ifdef HAVE_DOS_PATHS
     7153      if (isalpha(start[0]) && start[1] == ':')
     7154        start += 2;
     7155      else if (   IS_PATHSEP(start[0])
     7156               && IS_PATHSEP(start[1])
     7157               && !IS_PATHSEP(start[2]) && start[2] != '\0'
     7158               && start[3] != '\0')
     7159        {
     7160          /* Min recognized UNC: "//./" - find the next slash
     7161             Typical root: "//srv/shr/" */
     7162          /* XXX: Check if //./ needs special handling. */
     7163          start += 3;
     7164          while ((ch = *start) != '\0' && !IS_PATHSEP(ch))
     7165            start++;
     7166
     7167          if (IS_PATHSEP(ch) && !IS_PATHSEP(start[1]))
     7168            {
     7169              start++;
     7170              while ((ch = *start) != '\0' && !IS_PATHSEP(ch))
     7171                start++;
     7172            }
     7173          else
     7174            start = cur->name; /* invalid UNC, pretend it's a couple unixy root slashes. */
     7175        }
     7176
     7177# elif defined (VMS) || defined (AMGIA)
     7178      /* XXX: VMS and AMGIA */
     7179      OS (fatal, NILF, _("$(%s) is not implemented on this platform"), funcname);
     7180# endif
     7181
     7182      /* Exclude all subsequent / leading path separators. */
     7183      while ((ch = *start) != '\0' && IS_PATHSEP(ch))
     7184        start++;
     7185
     7186      if (ch != '\0')
     7187        o = helper_return_file(o, start, style, cur->next == NULL);
     7188      else
     7189        o = helper_return_file_len (o, ".", 1, style, cur->next == NULL);
     7190    }
     7191
     7192  free_ns_chain_no_strcache (chain);
     7193  return o;
     7194}
     7195
     7196# endif
     7197
     7198/* $(qrealpath path1 .. pathN [, style]) - same as $(realpath ), except files
     7199 rather than space delimited word tokens.  See func_realpath(). */
     7200
     7201static char *
     7202func_q_realpath (char *o, char **argv, const char *funcname UNUSED)
     7203{
     7204  PATH_VAR (outbuf);
     7205  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     7206  struct nameseq *chain = helper_parse_file_list (argv[0], style, 0);
     7207
     7208  /* Pass one: Do the realpath/abspath thing and remove anything that fails
     7209               or doesn't exists. */
     7210  struct nameseq *cur = chain;
     7211  struct nameseq *prev = NULL;
     7212  while (cur)
     7213    {
     7214      char *result;
     7215#ifdef HAVE_REALPATH
     7216      ENULLLOOP (result, realpath (cur->name, outbuf));
     7217#else
     7218      result = abspath (cur->name, outbuf);
     7219#endif
     7220      if (result)
     7221        {
     7222           struct stat st;
     7223           int r;
     7224           EINTRLOOP (r, stat (outbuf, &st));
     7225           if (r == 0)
     7226             {
     7227               free ((char *)cur->name);
     7228               cur->name = xstrdup (result);
     7229               prev = cur;
     7230               cur = cur->next;
     7231             }
     7232           else
     7233             cur = helper_unlink_and_free_ns(cur, prev, &chain);
     7234        }
     7235      else
     7236        cur = helper_unlink_and_free_ns(cur, prev, &chain);
     7237    }
     7238
     7239  /* Pass two: Output. */
     7240  return helper_return_and_free_chain (o, chain, style);
     7241}
     7242
     7243/* $(qwildcard path1 .. pathN [, style]) - same as $(wildcard ), except files
     7244 rather than space delimited word tokens.  See func_wildcard(). */
     7245
     7246static char *
     7247func_q_wildcard (char *o, char **argv, const char *funcname UNUSED)
     7248{
     7249  unsigned int const style = helper_file_return_style (argv[1], Q_QDEFAULT);
     7250  struct nameseq *chain = helper_parse_file_list (argv[0], style, 1 /*glob*/);
     7251#ifdef _AMIGA
     7252  OS (fatal, NILF, _("$(%s ) is not implemented on this platform"), funcname);
     7253#endif
     7254  return helper_return_and_free_chain (o, chain, style);
     7255}
     7256
    58017257#endif /* KMK */
    58027258
     
    59947450  FT_ENTRY ("set-umask",     1,  3,  1,  func_set_umask),
    59957451  FT_ENTRY ("get-umask",     0,  0,  0,  func_get_umask),
     7452#endif
     7453#ifdef KMK
     7454  FT_ENTRY ("quote",         1,  0,  1,  func_quote_make),
     7455  FT_ENTRY ("quote-dep",     1,  0,  1,  func_quote_make),
     7456  FT_ENTRY ("quote-tgt",     1,  0,  1,  func_quote_make),
     7457  FT_ENTRY ("quote-depend",  1,  0,  1,  func_quote_make),
     7458  FT_ENTRY ("quote-tgtend",  1,  0,  1,  func_quote_make),
     7459  FT_ENTRY ("quote-sh",      1,  0,  1,  func_quote_shell),
     7460  FT_ENTRY ("quote-sh-dq",   1,  1,  1,  func_quote_shell_dq),
     7461  FT_ENTRY ("quote-sh-sq",   1,  1,  1,  func_quote_shell_sq),
     7462  /* Quoted input and maybe output variants of functions typically
     7463     working with files: */
     7464  FT_ENTRY ("firstfile",     0, 1+1, 1,  func_firstfile),
     7465  FT_ENTRY ("lastfile",      0, 1+1, 1,  func_lastfile),
     7466  FT_ENTRY ("filelist",      3, 3+1, 1,  func_filelist),
     7467  FT_ENTRY ("countfiles",    0, 1+1, 1,  func_countfiles),
     7468  FT_ENTRY ("foreachfile",   3, 3+1, 0,  func_foreachfile),
     7469  FT_ENTRY ("sortfiles",     0, 1+1, 1,  func_sortfiles),
     7470# ifdef CONFIG_WITH_RSORT
     7471  FT_ENTRY ("rsortfiles",    0, 1+1, 1,  func_sortfiles),
     7472# endif
     7473  FT_ENTRY ("qabspath",      0, 1+1, 1,  func_q_abspath),
     7474  FT_ENTRY ("qaddprefix",    2, 2+1, 1,  func_q_addsuffix_addprefix),
     7475  FT_ENTRY ("qaddsuffix",    2, 2+1, 1,  func_q_addsuffix_addprefix),
     7476  FT_ENTRY ("qbasename",     0, 1+1, 1,  func_q_basename_dir),
     7477  FT_ENTRY ("qdir",          0, 1+1, 1,  func_q_basename_dir),
     7478  FT_ENTRY ("qnotdir",       0, 1+1, 1,  func_q_notdir),
     7479///@todo # ifdef CONFIG_WITH_ROOT_FUNC
     7480///@todo   FT_ENTRY ("qroot",         0, 1+1, 1,  func_q_root),
     7481///@todo   FT_ENTRY ("qnotroot",      0, 1+1, 1,  func_q_notroot),
     7482///@todo # endif
     7483  FT_ENTRY ("qsuffix",       0, 1+1, 1,  func_q_suffix),
     7484  FT_ENTRY ("qrealpath",     0, 1+1, 1,  func_q_realpath),
     7485# ifdef CONFIG_WITH_ABSPATHEX
     7486  FT_ENTRY ("qabspathex",    0, 2+1, 1,  func_q_abspathex),
     7487# endif
     7488  FT_ENTRY ("qwildcard",     0, 1+1, 1,  func_q_wildcard),
    59967489#endif
    59977490};
Note: See TracChangeset for help on using the changeset viewer.