(root)/
xz-5.4.5/
src/
xz/
options.c
       1  ///////////////////////////////////////////////////////////////////////////////
       2  //
       3  /// \file       options.c
       4  /// \brief      Parser for filter-specific options
       5  //
       6  //  Author:     Lasse Collin
       7  //
       8  //  This file has been put into the public domain.
       9  //  You can do whatever you want with this file.
      10  //
      11  ///////////////////////////////////////////////////////////////////////////////
      12  
      13  #include "private.h"
      14  
      15  
      16  ///////////////////
      17  // Generic stuff //
      18  ///////////////////
      19  
      20  typedef struct {
      21  	const char *name;
      22  	uint64_t id;
      23  } name_id_map;
      24  
      25  
      26  typedef struct {
      27  	const char *name;
      28  	const name_id_map *map;
      29  	uint64_t min;
      30  	uint64_t max;
      31  } option_map;
      32  
      33  
      34  /// Parses option=value pairs that are separated with commas:
      35  /// opt=val,opt=val,opt=val
      36  ///
      37  /// Each option is a string, that is converted to an integer using the
      38  /// index where the option string is in the array.
      39  ///
      40  /// Value can be
      41  ///  - a string-id map mapping a list of possible string values to integers
      42  ///    (opts[i].map != NULL, opts[i].min and opts[i].max are ignored);
      43  ///  - a number with minimum and maximum value limit
      44  ///    (opts[i].map == NULL && opts[i].min != UINT64_MAX);
      45  ///  - a string that will be parsed by the filter-specific code
      46  ///    (opts[i].map == NULL && opts[i].min == UINT64_MAX, opts[i].max ignored)
      47  ///
      48  /// When parsing both option and value succeed, a filter-specific function
      49  /// is called, which should update the given value to filter-specific
      50  /// options structure.
      51  ///
      52  /// This returns only if no errors occur.
      53  ///
      54  /// \param      str     String containing the options from the command line
      55  /// \param      opts    Filter-specific option map
      56  /// \param      set     Filter-specific function to update filter_options
      57  /// \param      filter_options  Pointer to filter-specific options structure
      58  ///
      59  static void
      60  parse_options(const char *str, const option_map *opts,
      61  		void (*set)(void *filter_options,
      62  			unsigned key, uint64_t value, const char *valuestr),
      63  		void *filter_options)
      64  {
      65  	if (str == NULL || str[0] == '\0')
      66  		return;
      67  
      68  	char *s = xstrdup(str);
      69  	char *name = s;
      70  
      71  	while (*name != '\0') {
      72  		if (*name == ',') {
      73  			++name;
      74  			continue;
      75  		}
      76  
      77  		char *split = strchr(name, ',');
      78  		if (split != NULL)
      79  			*split = '\0';
      80  
      81  		char *value = strchr(name, '=');
      82  		if (value != NULL)
      83  			*value++ = '\0';
      84  
      85  		if (value == NULL || value[0] == '\0')
      86  			message_fatal(_("%s: Options must be `name=value' "
      87  					"pairs separated with commas"), str);
      88  
      89  		// Look for the option name from the option map.
      90  		unsigned i = 0;
      91  		while (true) {
      92  			if (opts[i].name == NULL)
      93  				message_fatal(_("%s: Invalid option name"),
      94  						name);
      95  
      96  			if (strcmp(name, opts[i].name) == 0)
      97  				break;
      98  
      99  			++i;
     100  		}
     101  
     102  		// Option was found from the map. See how we should handle it.
     103  		if (opts[i].map != NULL) {
     104  			// value is a string which we should map
     105  			// to an integer.
     106  			unsigned j;
     107  			for (j = 0; opts[i].map[j].name != NULL; ++j) {
     108  				if (strcmp(opts[i].map[j].name, value) == 0)
     109  					break;
     110  			}
     111  
     112  			if (opts[i].map[j].name == NULL)
     113  				message_fatal(_("%s: Invalid option value"),
     114  						value);
     115  
     116  			set(filter_options, i, opts[i].map[j].id, value);
     117  
     118  		} else if (opts[i].min == UINT64_MAX) {
     119  			// value is a special string that will be
     120  			// parsed by set().
     121  			set(filter_options, i, 0, value);
     122  
     123  		} else {
     124  			// value is an integer.
     125  			const uint64_t v = str_to_uint64(name, value,
     126  					opts[i].min, opts[i].max);
     127  			set(filter_options, i, v, value);
     128  		}
     129  
     130  		// Check if it was the last option.
     131  		if (split == NULL)
     132  			break;
     133  
     134  		name = split + 1;
     135  	}
     136  
     137  	free(s);
     138  	return;
     139  }
     140  
     141  
     142  ///////////
     143  // Delta //
     144  ///////////
     145  
     146  enum {
     147  	OPT_DIST,
     148  };
     149  
     150  
     151  static void
     152  set_delta(void *options, unsigned key, uint64_t value,
     153  		const char *valuestr lzma_attribute((__unused__)))
     154  {
     155  	lzma_options_delta *opt = options;
     156  	switch (key) {
     157  	case OPT_DIST:
     158  		opt->dist = value;
     159  		break;
     160  	}
     161  }
     162  
     163  
     164  extern lzma_options_delta *
     165  options_delta(const char *str)
     166  {
     167  	static const option_map opts[] = {
     168  		{ "dist",     NULL,  LZMA_DELTA_DIST_MIN,
     169  		                     LZMA_DELTA_DIST_MAX },
     170  		{ NULL,       NULL,  0, 0 }
     171  	};
     172  
     173  	lzma_options_delta *options = xmalloc(sizeof(lzma_options_delta));
     174  	*options = (lzma_options_delta){
     175  		// It's hard to give a useful default for this.
     176  		.type = LZMA_DELTA_TYPE_BYTE,
     177  		.dist = LZMA_DELTA_DIST_MIN,
     178  	};
     179  
     180  	parse_options(str, opts, &set_delta, options);
     181  
     182  	return options;
     183  }
     184  
     185  
     186  /////////
     187  // BCJ //
     188  /////////
     189  
     190  enum {
     191  	OPT_START_OFFSET,
     192  };
     193  
     194  
     195  static void
     196  set_bcj(void *options, unsigned key, uint64_t value,
     197  		const char *valuestr lzma_attribute((__unused__)))
     198  {
     199  	lzma_options_bcj *opt = options;
     200  	switch (key) {
     201  	case OPT_START_OFFSET:
     202  		opt->start_offset = value;
     203  		break;
     204  	}
     205  }
     206  
     207  
     208  extern lzma_options_bcj *
     209  options_bcj(const char *str)
     210  {
     211  	static const option_map opts[] = {
     212  		{ "start",    NULL,  0, UINT32_MAX },
     213  		{ NULL,       NULL,  0, 0 }
     214  	};
     215  
     216  	lzma_options_bcj *options = xmalloc(sizeof(lzma_options_bcj));
     217  	*options = (lzma_options_bcj){
     218  		.start_offset = 0,
     219  	};
     220  
     221  	parse_options(str, opts, &set_bcj, options);
     222  
     223  	return options;
     224  }
     225  
     226  
     227  //////////
     228  // LZMA //
     229  //////////
     230  
     231  enum {
     232  	OPT_PRESET,
     233  	OPT_DICT,
     234  	OPT_LC,
     235  	OPT_LP,
     236  	OPT_PB,
     237  	OPT_MODE,
     238  	OPT_NICE,
     239  	OPT_MF,
     240  	OPT_DEPTH,
     241  };
     242  
     243  
     244  tuklib_attr_noreturn
     245  static void
     246  error_lzma_preset(const char *valuestr)
     247  {
     248  	message_fatal(_("Unsupported LZMA1/LZMA2 preset: %s"), valuestr);
     249  }
     250  
     251  
     252  static void
     253  set_lzma(void *options, unsigned key, uint64_t value, const char *valuestr)
     254  {
     255  	lzma_options_lzma *opt = options;
     256  
     257  	switch (key) {
     258  	case OPT_PRESET: {
     259  		if (valuestr[0] < '0' || valuestr[0] > '9')
     260  			error_lzma_preset(valuestr);
     261  
     262  		uint32_t preset = (uint32_t)(valuestr[0] - '0');
     263  
     264  		// Currently only "e" is supported as a modifier,
     265  		// so keep this simple for now.
     266  		if (valuestr[1] != '\0') {
     267  			if (valuestr[1] == 'e')
     268  				preset |= LZMA_PRESET_EXTREME;
     269  			else
     270  				error_lzma_preset(valuestr);
     271  
     272  			if (valuestr[2] != '\0')
     273  				error_lzma_preset(valuestr);
     274  		}
     275  
     276  		if (lzma_lzma_preset(options, preset))
     277  			error_lzma_preset(valuestr);
     278  
     279  		break;
     280  	}
     281  
     282  	case OPT_DICT:
     283  		opt->dict_size = value;
     284  		break;
     285  
     286  	case OPT_LC:
     287  		opt->lc = value;
     288  		break;
     289  
     290  	case OPT_LP:
     291  		opt->lp = value;
     292  		break;
     293  
     294  	case OPT_PB:
     295  		opt->pb = value;
     296  		break;
     297  
     298  	case OPT_MODE:
     299  		opt->mode = value;
     300  		break;
     301  
     302  	case OPT_NICE:
     303  		opt->nice_len = value;
     304  		break;
     305  
     306  	case OPT_MF:
     307  		opt->mf = value;
     308  		break;
     309  
     310  	case OPT_DEPTH:
     311  		opt->depth = value;
     312  		break;
     313  	}
     314  }
     315  
     316  
     317  extern lzma_options_lzma *
     318  options_lzma(const char *str)
     319  {
     320  	static const name_id_map modes[] = {
     321  		{ "fast",   LZMA_MODE_FAST },
     322  		{ "normal", LZMA_MODE_NORMAL },
     323  		{ NULL,     0 }
     324  	};
     325  
     326  	static const name_id_map mfs[] = {
     327  		{ "hc3", LZMA_MF_HC3 },
     328  		{ "hc4", LZMA_MF_HC4 },
     329  		{ "bt2", LZMA_MF_BT2 },
     330  		{ "bt3", LZMA_MF_BT3 },
     331  		{ "bt4", LZMA_MF_BT4 },
     332  		{ NULL,  0 }
     333  	};
     334  
     335  	static const option_map opts[] = {
     336  		{ "preset", NULL,   UINT64_MAX, 0 },
     337  		{ "dict",   NULL,   LZMA_DICT_SIZE_MIN,
     338  				(UINT32_C(1) << 30) + (UINT32_C(1) << 29) },
     339  		{ "lc",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
     340  		{ "lp",     NULL,   LZMA_LCLP_MIN, LZMA_LCLP_MAX },
     341  		{ "pb",     NULL,   LZMA_PB_MIN, LZMA_PB_MAX },
     342  		{ "mode",   modes,  0, 0 },
     343  		{ "nice",   NULL,   2, 273 },
     344  		{ "mf",     mfs,    0, 0 },
     345  		{ "depth",  NULL,   0, UINT32_MAX },
     346  		{ NULL,     NULL,   0, 0 }
     347  	};
     348  
     349  	lzma_options_lzma *options = xmalloc(sizeof(lzma_options_lzma));
     350  	if (lzma_lzma_preset(options, LZMA_PRESET_DEFAULT))
     351  		message_bug();
     352  
     353  	parse_options(str, opts, &set_lzma, options);
     354  
     355  	if (options->lc + options->lp > LZMA_LCLP_MAX)
     356  		message_fatal(_("The sum of lc and lp must not exceed 4"));
     357  
     358  	return options;
     359  }