source: valtobtest/subversion-1.6.2/subversion/libsvn_subr/opt.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 30.1 KB
Line 
1/*
2 * opt.c :  option and argument parsing for Subversion command lines
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
6 *
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
12 *
13 * This software consists of voluntary contributions made by many
14 * individuals.  For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
17 */
18
19
20
21#define APR_WANT_STRFUNC
22#include <apr_want.h>
23
24#include <stdio.h>
25#include <assert.h>
26#include <apr_pools.h>
27#include <apr_general.h>
28#include <apr_lib.h>
29#include <apr_file_info.h>
30
31#include "svn_cmdline.h"
32#include "svn_version.h"
33#include "svn_types.h"
34#include "svn_opt.h"
35#include "svn_error.h"
36#include "svn_path.h"
37#include "svn_utf.h"
38#include "svn_time.h"
39#include "svn_props.h"
40
41#include "private/svn_opt_private.h"
42
43#include "opt.h"
44#include "svn_private_config.h"
45
46
47/*** Code. ***/
48
49const svn_opt_subcommand_desc2_t *
50svn_opt_get_canonical_subcommand2(const svn_opt_subcommand_desc2_t *table,
51                                  const char *cmd_name)
52{
53  int i = 0;
54
55  if (cmd_name == NULL)
56    return NULL;
57
58  while (table[i].name) {
59    int j;
60    if (strcmp(cmd_name, table[i].name) == 0)
61      return table + i;
62    for (j = 0; (j < SVN_OPT_MAX_ALIASES) && table[i].aliases[j]; j++)
63      if (strcmp(cmd_name, table[i].aliases[j]) == 0)
64        return table + i;
65
66    i++;
67  }
68
69  /* If we get here, there was no matching subcommand name or alias. */
70  return NULL;
71}
72
73const apr_getopt_option_t *
74svn_opt_get_option_from_code2(int code,
75                              const apr_getopt_option_t *option_table,
76                              const svn_opt_subcommand_desc2_t *command,
77                              apr_pool_t *pool)
78{
79  apr_size_t i;
80
81  for (i = 0; option_table[i].optch; i++)
82    if (option_table[i].optch == code)
83      {
84        int j;
85        if (command)
86          for (j = 0; ((j < SVN_OPT_MAX_OPTIONS) &&
87                       command->desc_overrides[j].optch); j++)
88            if (command->desc_overrides[j].optch == code)
89              {
90                apr_getopt_option_t *tmpopt =
91                    apr_palloc(pool, sizeof(*tmpopt));
92                *tmpopt = option_table[i];
93                tmpopt->description = command->desc_overrides[j].desc;
94                return tmpopt;
95              }
96        return &(option_table[i]);
97      }
98
99  return NULL;
100}
101
102
103const apr_getopt_option_t *
104svn_opt_get_option_from_code(int code,
105                             const apr_getopt_option_t *option_table)
106{
107  apr_size_t i;
108
109  for (i = 0; option_table[i].optch; i++)
110    if (option_table[i].optch == code)
111      return &(option_table[i]);
112
113  return NULL;
114}
115
116
117svn_boolean_t
118svn_opt_subcommand_takes_option3(const svn_opt_subcommand_desc2_t *command,
119                                 int option_code,
120                                 const int *global_options)
121{
122  apr_size_t i;
123
124  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
125    if (command->valid_options[i] == option_code)
126      return TRUE;
127
128  if (global_options)
129    for (i = 0; global_options[i]; i++)
130      if (global_options[i] == option_code)
131        return TRUE;
132
133  return FALSE;
134}
135
136svn_boolean_t
137svn_opt_subcommand_takes_option2(const svn_opt_subcommand_desc2_t *command,
138                                 int option_code)
139{
140  return svn_opt_subcommand_takes_option3(command,
141                                          option_code,
142                                          NULL);
143}
144
145
146svn_boolean_t
147svn_opt_subcommand_takes_option(const svn_opt_subcommand_desc_t *command,
148                                int option_code)
149{
150  apr_size_t i;
151
152  for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
153    if (command->valid_options[i] == option_code)
154      return TRUE;
155
156  return FALSE;
157}
158
159
160/* Print the canonical command name for CMD, and all its aliases, to
161   STREAM.  If HELP is set, print CMD's help string too, in which case
162   obtain option usage from OPTIONS_TABLE. */
163static svn_error_t *
164print_command_info2(const svn_opt_subcommand_desc2_t *cmd,
165                    const apr_getopt_option_t *options_table,
166                    const int *global_options,
167                    svn_boolean_t help,
168                    apr_pool_t *pool,
169                    FILE *stream)
170{
171  svn_boolean_t first_time;
172  apr_size_t i;
173
174  /* Print the canonical command name. */
175  SVN_ERR(svn_cmdline_fputs(cmd->name, stream, pool));
176
177  /* Print the list of aliases. */
178  first_time = TRUE;
179  for (i = 0; i < SVN_OPT_MAX_ALIASES; i++)
180    {
181      if (cmd->aliases[i] == NULL)
182        break;
183
184      if (first_time) {
185        SVN_ERR(svn_cmdline_fputs(" (", stream, pool));
186        first_time = FALSE;
187      }
188      else
189        SVN_ERR(svn_cmdline_fputs(", ", stream, pool));
190
191      SVN_ERR(svn_cmdline_fputs(cmd->aliases[i], stream, pool));
192    }
193
194  if (! first_time)
195    SVN_ERR(svn_cmdline_fputs(")", stream, pool));
196
197  if (help)
198    {
199      const apr_getopt_option_t *option;
200      svn_boolean_t have_options = FALSE;
201
202      SVN_ERR(svn_cmdline_fprintf(stream, pool, ": %s", _(cmd->help)));
203
204      /* Loop over all valid option codes attached to the subcommand */
205      for (i = 0; i < SVN_OPT_MAX_OPTIONS; i++)
206        {
207          if (cmd->valid_options[i])
208            {
209              if (have_options == FALSE)
210                {
211                  SVN_ERR(svn_cmdline_fputs(_("\nValid options:\n"),
212                                            stream, pool));
213                  have_options = TRUE;
214                }
215
216              /* convert each option code into an option */
217              option =
218                svn_opt_get_option_from_code2(cmd->valid_options[i],
219                                              options_table,
220                                              cmd, pool);
221
222              /* print the option's docstring */
223              if (option && option->description)
224                {
225                  const char *optstr;
226                  svn_opt_format_option(&optstr, option, TRUE, pool);
227                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
228                                              optstr));
229                }
230            }
231        }
232      /* And global options too */
233      if (global_options && *global_options)
234        {
235          SVN_ERR(svn_cmdline_fputs(_("\nGlobal options:\n"),
236                                    stream, pool));
237          have_options = TRUE;
238
239          for (i = 0; global_options[i]; i++)
240            {
241
242              /* convert each option code into an option */
243              option =
244                svn_opt_get_option_from_code2(global_options[i],
245                                              options_table,
246                                              cmd, pool);
247
248              /* print the option's docstring */
249              if (option && option->description)
250                {
251                  const char *optstr;
252                  svn_opt_format_option(&optstr, option, TRUE, pool);
253                  SVN_ERR(svn_cmdline_fprintf(stream, pool, "  %s\n",
254                                              optstr));
255                }
256            }
257        }
258
259      if (have_options)
260        SVN_ERR(svn_cmdline_fprintf(stream, pool, "\n"));
261    }
262
263  return SVN_NO_ERROR;
264}
265
266void
267svn_opt_print_generic_help2(const char *header,
268                            const svn_opt_subcommand_desc2_t *cmd_table,
269                            const apr_getopt_option_t *opt_table,
270                            const char *footer,
271                            apr_pool_t *pool, FILE *stream)
272{
273  int i = 0;
274  svn_error_t *err;
275
276  if (header)
277    if ((err = svn_cmdline_fputs(header, stream, pool)))
278      goto print_error;
279
280  while (cmd_table[i].name)
281    {
282      if ((err = svn_cmdline_fputs("   ", stream, pool))
283          || (err = print_command_info2(cmd_table + i, opt_table,
284                                        NULL, FALSE,
285                                        pool, stream))
286          || (err = svn_cmdline_fputs("\n", stream, pool)))
287        goto print_error;
288      i++;
289    }
290
291  if ((err = svn_cmdline_fputs("\n", stream, pool)))
292    goto print_error;
293
294  if (footer)
295    if ((err = svn_cmdline_fputs(footer, stream, pool)))
296      goto print_error;
297
298  return;
299
300 print_error:
301  svn_handle_error2(err, stderr, FALSE, "svn: ");
302  svn_error_clear(err);
303}
304
305void
306svn_opt_format_option(const char **string,
307                      const apr_getopt_option_t *opt,
308                      svn_boolean_t doc,
309                      apr_pool_t *pool)
310{
311  char *opts;
312
313  if (opt == NULL)
314    {
315      *string = "?";
316      return;
317    }
318
319  /* We have a valid option which may or may not have a "short
320     name" (a single-character alias for the long option). */
321  if (opt->optch <= 255)
322    opts = apr_psprintf(pool, "-%c [--%s]", opt->optch, opt->name);
323  else
324    opts = apr_psprintf(pool, "--%s", opt->name);
325
326  if (opt->has_arg)
327    opts = apr_pstrcat(pool, opts, _(" ARG"), NULL);
328
329  if (doc)
330    opts = apr_psprintf(pool, "%-24s : %s", opts, _(opt->description));
331
332  *string = opts;
333}
334
335
336void
337svn_opt_subcommand_help3(const char *subcommand,
338                         const svn_opt_subcommand_desc2_t *table,
339                         const apr_getopt_option_t *options_table,
340                         const int *global_options,
341                         apr_pool_t *pool)
342{
343  const svn_opt_subcommand_desc2_t *cmd =
344    svn_opt_get_canonical_subcommand2(table, subcommand);
345  svn_error_t *err;
346
347  if (cmd)
348    err = print_command_info2(cmd, options_table, global_options,
349                              TRUE, pool, stdout);
350  else
351    err = svn_cmdline_fprintf(stderr, pool,
352                              _("\"%s\": unknown command.\n\n"), subcommand);
353
354  if (err) {
355    svn_handle_error2(err, stderr, FALSE, "svn: ");
356    svn_error_clear(err);
357  }
358}
359
360
361
362/*** Parsing revision and date options. ***/
363
364
365/** Parsing "X:Y"-style arguments. **/
366
367/* If WORD matches one of the special revision descriptors,
368 * case-insensitively, set *REVISION accordingly:
369 *
370 *   - For "head", set REVISION->kind to svn_opt_revision_head.
371 *
372 *   - For "prev", set REVISION->kind to svn_opt_revision_previous.
373 *
374 *   - For "base", set REVISION->kind to svn_opt_revision_base.
375 *
376 *   - For "committed", set REVISION->kind to svn_opt_revision_committed.
377 *
378 * If match, return 0, else return -1 and don't touch REVISION.
379 */
380static int
381revision_from_word(svn_opt_revision_t *revision, const char *word)
382{
383  if (svn_cstring_casecmp(word, "head") == 0)
384    {
385      revision->kind = svn_opt_revision_head;
386    }
387  else if (svn_cstring_casecmp(word, "prev") == 0)
388    {
389      revision->kind = svn_opt_revision_previous;
390    }
391  else if (svn_cstring_casecmp(word, "base") == 0)
392    {
393      revision->kind = svn_opt_revision_base;
394    }
395  else if (svn_cstring_casecmp(word, "committed") == 0)
396    {
397      revision->kind = svn_opt_revision_committed;
398    }
399  else
400    return -1;
401
402  return 0;
403}
404
405
406/* Parse one revision specification.  Return pointer to character
407   after revision, or NULL if the revision is invalid.  Modifies
408   str, so make sure to pass a copy of anything precious.  Uses
409   POOL for temporary allocation. */
410static char *parse_one_rev(svn_opt_revision_t *revision, char *str,
411                           apr_pool_t *pool)
412{
413  char *end, save;
414
415  /* Allow any number of 'r's to prefix a revision number, because
416     that way if a script pastes svn output into another svn command
417     (like "svn log -r${REV_COPIED_FROM_OUTPUT}"), it'll Just Work,
418     even when compounded.
419
420     As it happens, none of our special revision words begins with
421     "r".  If any ever do, then this code will have to get smarter.
422
423     Incidentally, this allows "r{DATE}".  We could avoid that with
424     some trivial code rearrangement, but it's not clear what would
425     be gained by doing so. */
426  while (*str == 'r')
427    str++;
428
429  if (*str == '{')
430    {
431      svn_boolean_t matched;
432      apr_time_t tm;
433      svn_error_t *err;
434
435      /* Brackets denote a date. */
436      str++;
437      end = strchr(str, '}');
438      if (!end)
439        return NULL;
440      *end = '\0';
441      err = svn_parse_date(&matched, &tm, str, apr_time_now(), pool);
442      if (err)
443        {
444          svn_error_clear(err);
445          return NULL;
446        }
447      if (!matched)
448        return NULL;
449      revision->kind = svn_opt_revision_date;
450      revision->value.date = tm;
451      return end + 1;
452    }
453  else if (apr_isdigit(*str))
454    {
455      /* It's a number. */
456      end = str + 1;
457      while (apr_isdigit(*end))
458        end++;
459      save = *end;
460      *end = '\0';
461      revision->kind = svn_opt_revision_number;
462      revision->value.number = SVN_STR_TO_REV(str);
463      *end = save;
464      return end;
465    }
466  else if (apr_isalpha(*str))
467    {
468      end = str + 1;
469      while (apr_isalpha(*end))
470        end++;
471      save = *end;
472      *end = '\0';
473      if (revision_from_word(revision, str) != 0)
474        return NULL;
475      *end = save;
476      return end;
477    }
478  else
479    return NULL;
480}
481
482
483int
484svn_opt_parse_revision(svn_opt_revision_t *start_revision,
485                       svn_opt_revision_t *end_revision,
486                       const char *arg,
487                       apr_pool_t *pool)
488{
489  char *left_rev, *right_rev, *end;
490
491  /* Operate on a copy of the argument. */
492  left_rev = apr_pstrdup(pool, arg);
493
494  right_rev = parse_one_rev(start_revision, left_rev, pool);
495  if (right_rev && *right_rev == ':')
496    {
497      right_rev++;
498      end = parse_one_rev(end_revision, right_rev, pool);
499      if (!end || *end != '\0')
500        return -1;
501    }
502  else if (!right_rev || *right_rev != '\0')
503    return -1;
504
505  return 0;
506}
507
508
509int
510svn_opt_parse_revision_to_range(apr_array_header_t *opt_ranges,
511                                const char *arg,
512                                apr_pool_t *pool)
513{
514  svn_opt_revision_range_t *range = apr_palloc(pool, sizeof(*range));
515
516  range->start.kind = svn_opt_revision_unspecified;
517  range->end.kind = svn_opt_revision_unspecified;
518
519  if (svn_opt_parse_revision(&(range->start), &(range->end),
520                             arg, pool) == -1)
521    return -1;
522
523  APR_ARRAY_PUSH(opt_ranges, svn_opt_revision_range_t *) = range;
524  return 0;
525}
526
527svn_error_t *
528svn_opt_resolve_revisions(svn_opt_revision_t *peg_rev,
529                          svn_opt_revision_t *op_rev,
530                          svn_boolean_t is_url,
531                          svn_boolean_t notice_local_mods,
532                          apr_pool_t *pool)
533{
534  if (peg_rev->kind == svn_opt_revision_unspecified)
535    {
536      if (is_url)
537        {
538          peg_rev->kind = svn_opt_revision_head;
539        }
540      else
541        {
542          if (notice_local_mods)
543            peg_rev->kind = svn_opt_revision_working;
544          else
545            peg_rev->kind = svn_opt_revision_base;
546        }
547    }
548
549  if (op_rev->kind == svn_opt_revision_unspecified)
550    *op_rev = *peg_rev;
551
552  return SVN_NO_ERROR;
553}
554
555
556/*** Parsing arguments. ***/
557#define DEFAULT_ARRAY_SIZE 5
558
559
560/* Copy STR into POOL and push the copy onto ARRAY. */
561static void
562array_push_str(apr_array_header_t *array,
563               const char *str,
564               apr_pool_t *pool)
565{
566  /* ### Not sure if this function is still necessary.  It used to
567     convert str to svn_stringbuf_t * and push it, but now it just
568     dups str in pool and pushes the copy.  So its only effect is
569     transfer str's lifetime to pool.  Is that something callers are
570     depending on? */
571
572  APR_ARRAY_PUSH(array, const char *) = apr_pstrdup(pool, str);
573}
574
575
576void
577svn_opt_push_implicit_dot_target(apr_array_header_t *targets,
578                                 apr_pool_t *pool)
579{
580  if (targets->nelts == 0)
581    array_push_str(targets, "", pool); /* Ha! "", not ".", is the canonical */
582  assert(targets->nelts);
583}
584
585
586svn_error_t *
587svn_opt_parse_num_args(apr_array_header_t **args_p,
588                       apr_getopt_t *os,
589                       int num_args,
590                       apr_pool_t *pool)
591{
592  int i;
593  apr_array_header_t *args
594    = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
595
596  /* loop for num_args and add each arg to the args array */
597  for (i = 0; i < num_args; i++)
598    {
599      if (os->ind >= os->argc)
600        {
601          return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
602        }
603      array_push_str(args, os->argv[os->ind++], pool);
604    }
605
606  *args_p = args;
607  return SVN_NO_ERROR;
608}
609
610svn_error_t *
611svn_opt_parse_all_args(apr_array_header_t **args_p,
612                       apr_getopt_t *os,
613                       apr_pool_t *pool)
614{
615  apr_array_header_t *args
616    = apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
617
618  if (os->ind > os->argc)
619    {
620      return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
621    }
622  while (os->ind < os->argc)
623    {
624      array_push_str(args, os->argv[os->ind++], pool);
625    }
626
627  *args_p = args;
628  return SVN_NO_ERROR;
629}
630
631
632svn_error_t *
633svn_opt_parse_path(svn_opt_revision_t *rev,
634                   const char **truepath,
635                   const char *path /* UTF-8! */,
636                   apr_pool_t *pool)
637{
638  const char *peg_rev;
639
640  SVN_ERR(svn_opt__split_arg_at_peg_revision(truepath, &peg_rev, path, pool));
641
642  /* Parse the peg revision, if one was found */
643  if (strlen(peg_rev))
644    {
645      int ret;
646      svn_opt_revision_t start_revision, end_revision;
647
648      end_revision.kind = svn_opt_revision_unspecified;
649
650      if (peg_rev[1] == '\0')  /* looking at empty peg revision */
651        {
652          ret = 0;
653          start_revision.kind = svn_opt_revision_unspecified;
654        }
655      else  /* looking at non-empty peg revision */
656        {
657          const char *rev_str = &peg_rev[1];
658
659          /* URLs get treated differently from wc paths. */
660          if (svn_path_is_url(path))
661            {
662              /* URLs are URI-encoded, so we look for dates with
663                 URI-encoded delimeters.  */
664              int rev_len = strlen(rev_str);
665              if (rev_len > 6
666                  && rev_str[0] == '%'
667                  && rev_str[1] == '7'
668                  && (rev_str[2] == 'B'
669                      || rev_str[2] == 'b')
670                  && rev_str[rev_len-3] == '%'
671                  && rev_str[rev_len-2] == '7'
672                  && (rev_str[rev_len-1] == 'D'
673                      || rev_str[rev_len-1] == 'd'))
674                {
675                  rev_str = svn_path_uri_decode(rev_str, pool);
676                }
677            }
678          ret = svn_opt_parse_revision(&start_revision,
679                                       &end_revision,
680                                       rev_str, pool);
681        }
682
683      if (ret || end_revision.kind != svn_opt_revision_unspecified)
684        return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
685                                 _("Syntax error parsing revision '%s'"),
686                                 &peg_rev[1]);
687
688      rev->kind = start_revision.kind;
689      rev->value = start_revision.value;
690    }
691  else
692    {
693      /* Didn't find a peg revision. */
694      rev->kind = svn_opt_revision_unspecified;
695    }
696
697  return SVN_NO_ERROR;
698}
699
700
701/* Note: This is substantially copied into svn_client_args_to_target_array() in
702 * order to move to libsvn_client while maintaining backward compatibility. */
703svn_error_t *
704svn_opt__args_to_target_array(apr_array_header_t **targets_p,
705                              apr_getopt_t *os,
706                              apr_array_header_t *known_targets,
707                              apr_pool_t *pool)
708{
709  int i;
710  svn_error_t *err = SVN_NO_ERROR;
711  apr_array_header_t *input_targets =
712    apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
713  apr_array_header_t *output_targets =
714    apr_array_make(pool, DEFAULT_ARRAY_SIZE, sizeof(const char *));
715
716  /* Step 1:  create a master array of targets that are in UTF-8
717     encoding, and come from concatenating the targets left by apr_getopt,
718     plus any extra targets (e.g., from the --targets switch.) */
719
720  for (; os->ind < os->argc; os->ind++)
721    {
722      /* The apr_getopt targets are still in native encoding. */
723      const char *raw_target = os->argv[os->ind];
724      SVN_ERR(svn_utf_cstring_to_utf8
725              ((const char **) apr_array_push(input_targets),
726               raw_target, pool));
727    }
728
729  if (known_targets)
730    {
731      for (i = 0; i < known_targets->nelts; i++)
732        {
733          /* The --targets array have already been converted to UTF-8,
734             because we needed to split up the list with svn_cstring_split. */
735          const char *utf8_target = APR_ARRAY_IDX(known_targets,
736                                                  i, const char *);
737          APR_ARRAY_PUSH(input_targets, const char *) = utf8_target;
738        }
739    }
740
741  /* Step 2:  process each target.  */
742
743  for (i = 0; i < input_targets->nelts; i++)
744    {
745      const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *);
746      const char *true_target;
747      const char *target;      /* after all processing is finished */
748      const char *peg_rev;
749
750      /*
751       * This is needed so that the target can be properly canonicalized,
752       * otherwise the canonicalization does not treat a ".@BASE" as a "."
753       * with a BASE peg revision, and it is not canonicalized to "@BASE".
754       * If any peg revision exists, it is appended to the final
755       * canonicalized path or URL.  Do not use svn_opt_parse_path()
756       * because the resulting peg revision is a structure that would have
757       * to be converted back into a string.  Converting from a string date
758       * to the apr_time_t field in the svn_opt_revision_value_t and back to
759       * a string would not necessarily preserve the exact bytes of the
760       * input date, so its easier just to keep it in string form.
761       */
762      SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev,
763                                                 utf8_target, pool));
764
765      /* URLs and wc-paths get treated differently. */
766      if (svn_path_is_url(true_target))
767        {
768          SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, true_target,
769                                                 pool));
770        }
771      else  /* not a url, so treat as a path */
772        {
773          const char *base_name;
774
775          SVN_ERR(svn_opt__arg_canonicalize_path(&true_target, true_target,
776                                                 pool));
777
778          /* If the target has the same name as a Subversion
779             working copy administrative dir, skip it. */
780          base_name = svn_path_basename(true_target, pool);
781
782          /* FIXME:
783             The canonical list of administrative directory names is
784             maintained in libsvn_wc/adm_files.c:svn_wc_set_adm_dir().
785             That list can't be used here, because that use would
786             create a circular dependency between libsvn_wc and
787             libsvn_subr.  Make sure changes to the lists are always
788             synchronized! */
789          if (0 == strcmp(base_name, ".svn")
790              || 0 == strcmp(base_name, "_svn"))
791            {
792              err = svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED,
793                                      err, _("'%s' ends in a reserved name"),
794                                      utf8_target);
795              continue;
796            }
797        }
798
799      target = apr_pstrcat(pool, true_target, peg_rev, NULL);
800
801      APR_ARRAY_PUSH(output_targets, const char *) = target;
802    }
803
804
805  /* kff todo: need to remove redundancies from targets before
806     passing it to the cmd_func. */
807
808  *targets_p = output_targets;
809
810  return err;
811}
812
813svn_error_t *
814svn_opt_parse_revprop(apr_hash_t **revprop_table_p, const char *revprop_spec,
815                      apr_pool_t *pool)
816{
817  const char *sep, *propname;
818  svn_string_t *propval;
819
820  if (! *revprop_spec)
821    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
822                            _("Revision property pair is empty"));
823
824  if (! *revprop_table_p)
825    *revprop_table_p = apr_hash_make(pool);
826
827  sep = strchr(revprop_spec, '=');
828  if (sep)
829    {
830      propname = apr_pstrndup(pool, revprop_spec, sep - revprop_spec);
831      SVN_ERR(svn_utf_cstring_to_utf8(&propname, propname, pool));
832      propval = svn_string_create(sep + 1, pool);
833    }
834  else
835    {
836      SVN_ERR(svn_utf_cstring_to_utf8(&propname, revprop_spec, pool));
837      propval = svn_string_create("", pool);
838    }
839
840  if (!svn_prop_name_is_valid(propname))
841    return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
842                             _("'%s' is not a valid Subversion property name"),
843                             propname);
844
845  apr_hash_set(*revprop_table_p, propname, APR_HASH_KEY_STRING, propval);
846
847  return SVN_NO_ERROR;
848}
849
850svn_error_t *
851svn_opt__split_arg_at_peg_revision(const char **true_target,
852                                   const char **peg_revision,
853                                   const char *utf8_target,
854                                   apr_pool_t *pool)
855{
856  const char *peg_start = NULL; /* pointer to the peg revision, if any */
857  int j;
858
859  for (j = (strlen(utf8_target) - 1); j >= 0; --j)
860    {
861      /* If we hit a path separator, stop looking.  This is OK
862          only because our revision specifiers can't contain '/'. */
863      if (utf8_target[j] == '/')
864        break;
865
866      if (utf8_target[j] == '@')
867        {
868          peg_start = &utf8_target[j];
869          break;
870        }
871    }
872
873  if (peg_start)
874    {
875      *true_target = apr_pstrmemdup(pool, utf8_target, j);
876      *peg_revision = apr_pstrdup(pool, peg_start);
877    }
878  else
879    {
880      *true_target = utf8_target;
881      *peg_revision = "";
882    }
883
884  return SVN_NO_ERROR;
885}
886
887svn_error_t *
888svn_opt__arg_canonicalize_url(const char **url_out, const char *url_in,
889                              apr_pool_t *pool)
890{
891  const char *target;
892
893  /* Convert to URI. */
894  target = svn_path_uri_from_iri(url_in, pool);
895  /* Auto-escape some ASCII characters. */
896  target = svn_path_uri_autoescape(target, pool);
897
898  /* The above doesn't guarantee a valid URI. */
899  if (! svn_path_is_uri_safe(target))
900    return svn_error_createf(SVN_ERR_BAD_URL, 0,
901                             _("URL '%s' is not properly URI-encoded"),
902                             target);
903
904  /* Verify that no backpaths are present in the URL. */
905  if (svn_path_is_backpath_present(target))
906    return svn_error_createf(SVN_ERR_BAD_URL, 0,
907                             _("URL '%s' contains a '..' element"),
908                             target);
909
910  /* strip any trailing '/' and collapse other redundant elements */
911  target = svn_path_canonicalize(target, pool);
912
913  *url_out = target;
914  return SVN_NO_ERROR;
915}
916
917svn_error_t *
918svn_opt__arg_canonicalize_path(const char **path_out, const char *path_in,
919                               apr_pool_t *pool)
920{
921  const char *apr_target;
922  char *truenamed_target; /* APR-encoded */
923  apr_status_t apr_err;
924
925  /* canonicalize case, and change all separators to '/'. */
926  SVN_ERR(svn_path_cstring_from_utf8(&apr_target, path_in, pool));
927  apr_err = apr_filepath_merge(&truenamed_target, "", apr_target,
928                               APR_FILEPATH_TRUENAME, pool);
929
930  if (!apr_err)
931    /* We have a canonicalized APR-encoded target now. */
932    apr_target = truenamed_target;
933  else if (APR_STATUS_IS_ENOENT(apr_err))
934    /* It's okay for the file to not exist, that just means we
935       have to accept the case given to the client. We'll use
936       the original APR-encoded target. */
937    ;
938  else
939    return svn_error_createf(apr_err, NULL,
940                             _("Error resolving case of '%s'"),
941                             svn_path_local_style(path_in, pool));
942
943  /* convert back to UTF-8. */
944  SVN_ERR(svn_path_cstring_to_utf8(path_out, apr_target, pool));
945  *path_out = svn_path_canonicalize(*path_out, pool);
946
947  return SVN_NO_ERROR;
948}
949
950svn_error_t *
951svn_opt__print_version_info(const char *pgm_name,
952                            const char *footer,
953                            svn_boolean_t quiet,
954                            apr_pool_t *pool)
955{
956  if (quiet)
957    return svn_cmdline_printf(pool, "%s\n", SVN_VER_NUMBER);
958
959  SVN_ERR(svn_cmdline_printf(pool, _("%s, version %s\n"
960                                     "   compiled %s, %s\n\n"), pgm_name,
961                             SVN_VERSION, __DATE__, __TIME__));
962  SVN_ERR(svn_cmdline_fputs(_("Copyright (C) 2000-2009 CollabNet.\n"
963                              "Subversion is open source software, see"
964                              " http://subversion.tigris.org/\n"
965                              "This product includes software developed by "
966                              "CollabNet (http://www.Collab.Net/).\n\n"),
967                            stdout, pool));
968
969  if (footer)
970    {
971      SVN_ERR(svn_cmdline_printf(pool, "%s\n", footer));
972    }
973
974  return SVN_NO_ERROR;
975}
976
977
978svn_error_t *
979svn_opt_print_help3(apr_getopt_t *os,
980                    const char *pgm_name,
981                    svn_boolean_t print_version,
982                    svn_boolean_t quiet,
983                    const char *version_footer,
984                    const char *header,
985                    const svn_opt_subcommand_desc2_t *cmd_table,
986                    const apr_getopt_option_t *option_table,
987                    const int *global_options,
988                    const char *footer,
989                    apr_pool_t *pool)
990{
991  apr_array_header_t *targets = NULL;
992  int i;
993
994  if (os)
995    SVN_ERR(svn_opt_parse_all_args(&targets, os, pool));
996
997  if (os && targets->nelts)  /* help on subcommand(s) requested */
998    for (i = 0; i < targets->nelts; i++)
999      {
1000        svn_opt_subcommand_help3(APR_ARRAY_IDX(targets, i, const char *),
1001                                 cmd_table, option_table,
1002                                 global_options, pool);
1003      }
1004  else if (print_version)   /* just --version */
1005    SVN_ERR(svn_opt__print_version_info(pgm_name, version_footer, quiet,
1006                                        pool));
1007  else if (os && !targets->nelts)            /* `-h', `--help', or `help' */
1008    svn_opt_print_generic_help2(header,
1009                                cmd_table,
1010                                option_table,
1011                                footer,
1012                                pool,
1013                                stdout);
1014  else                                       /* unknown option or cmd */
1015    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
1016                                _("Type '%s help' for usage.\n"), pgm_name));
1017
1018  return SVN_NO_ERROR;
1019}
Note: See TracBrowser for help on using the repository browser.