(root)/
util-linux-2.39/
libsmartcols/
src/
column.c
       1  /*
       2   * column.c - functions for table handling at the column level
       3   *
       4   * Copyright (C) 2014 Ondrej Oprala <ooprala@redhat.com>
       5   * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
       6   *
       7   * This file may be redistributed under the terms of the
       8   * GNU Lesser General Public License.
       9   */
      10  
      11  /**
      12   * SECTION: column
      13   * @title: Column
      14   * @short_description: defines output columns formats, headers, etc.
      15   *
      16   * An API to access and modify per-column data and information.
      17   */
      18  
      19  
      20  #include <stdlib.h>
      21  #include <unistd.h>
      22  #include <string.h>
      23  #include <ctype.h>
      24  
      25  #include "mbsalign.h"
      26  #include "strutils.h"
      27  #include "smartcolsP.h"
      28  
      29  /**
      30   * scols_new_column:
      31   *
      32   * Allocates space for a new column.
      33   *
      34   * Returns: a pointer to a new struct libscols_column instance, NULL in case of an ENOMEM error.
      35   */
      36  struct libscols_column *scols_new_column(void)
      37  {
      38  	struct libscols_column *cl;
      39  
      40  	cl = calloc(1, sizeof(*cl));
      41  	if (!cl)
      42  		return NULL;
      43  	DBG(COL, ul_debugobj(cl, "alloc"));
      44  	cl->refcount = 1;
      45  	INIT_LIST_HEAD(&cl->cl_columns);
      46  	return cl;
      47  }
      48  
      49  /**
      50   * scols_ref_column:
      51   * @cl: a pointer to a struct libscols_column instance
      52   *
      53   * Increases the refcount of @cl.
      54   */
      55  void scols_ref_column(struct libscols_column *cl)
      56  {
      57  	if (cl)
      58  		cl->refcount++;
      59  }
      60  
      61  /**
      62   * scols_unref_column:
      63   * @cl: a pointer to a struct libscols_column instance
      64   *
      65   * Decreases the refcount of @cl. When the count falls to zero, the instance
      66   * is automatically deallocated.
      67   */
      68  void scols_unref_column(struct libscols_column *cl)
      69  {
      70  	if (cl && --cl->refcount <= 0) {
      71  		DBG(COL, ul_debugobj(cl, "dealloc"));
      72  		list_del(&cl->cl_columns);
      73  		scols_reset_cell(&cl->header);
      74  		free(cl->color);
      75  		free(cl->safechars);
      76  		free(cl->pending_data_buf);
      77  		free(cl->shellvar);
      78  		free(cl);
      79  	}
      80  }
      81  
      82  /**
      83   * scols_copy_column:
      84   * @cl: a pointer to a struct libscols_column instance
      85   *
      86   * Creates a new column and copies @cl's data over to it.
      87   *
      88   * Returns: a pointer to a new struct libscols_column instance.
      89   */
      90  struct libscols_column *scols_copy_column(const struct libscols_column *cl)
      91  {
      92  	struct libscols_column *ret;
      93  
      94  	if (!cl)
      95  		return NULL;
      96  	ret = scols_new_column();
      97  	if (!ret)
      98  		return NULL;
      99  
     100  	DBG(COL, ul_debugobj(cl, "copy"));
     101  
     102  	if (scols_column_set_color(ret, cl->color))
     103  		goto err;
     104  	if (scols_cell_copy_content(&ret->header, &cl->header))
     105  		goto err;
     106  
     107  	ret->width	= cl->width;
     108  	ret->width_hint	= cl->width_hint;
     109  	ret->flags	= cl->flags;
     110  	ret->is_groups  = cl->is_groups;
     111  
     112  	memcpy(&ret->wstat, &cl->wstat, sizeof(cl->wstat));
     113  
     114  	return ret;
     115  err:
     116  	scols_unref_column(ret);
     117  	return NULL;
     118  }
     119  
     120  /**
     121   * scols_column_set_whint:
     122   * @cl: a pointer to a struct libscols_column instance
     123   * @whint: a width hint
     124   *
     125   * Sets the width hint of column @cl to @whint. See scols_table_new_column().
     126   *
     127   * Returns: 0, a negative value in case of an error.
     128   */
     129  int scols_column_set_whint(struct libscols_column *cl, double whint)
     130  {
     131  	if (!cl)
     132  		return -EINVAL;
     133  
     134  	cl->width_hint = whint;
     135  	return 0;
     136  }
     137  
     138  /**
     139   * scols_column_get_whint:
     140   * @cl: a pointer to a struct libscols_column instance
     141   *
     142   * Returns: The width hint of column @cl, a negative value in case of an error.
     143   */
     144  double scols_column_get_whint(const struct libscols_column *cl)
     145  {
     146  	return cl->width_hint;
     147  }
     148  
     149  /**
     150   * scols_column_set_flags:
     151   * @cl: a pointer to a struct libscols_column instance
     152   * @flags: a flag mask
     153   *
     154   * Sets the flags of @cl to @flags.
     155   *
     156   * Returns: 0, a negative value in case of an error.
     157   */
     158  int scols_column_set_flags(struct libscols_column *cl, int flags)
     159  {
     160  	if (!cl)
     161  		return -EINVAL;
     162  
     163  	if (cl->table) {
     164  		if (!(cl->flags & SCOLS_FL_TREE) && (flags & SCOLS_FL_TREE))
     165  			cl->table->ntreecols++;
     166  		else if ((cl->flags & SCOLS_FL_TREE) && !(flags & SCOLS_FL_TREE))
     167  			cl->table->ntreecols--;
     168  	}
     169  
     170  	DBG(COL, ul_debugobj(cl, "setting flags from 0x%04x to 0x%04x", cl->flags, flags));
     171  	cl->flags = flags;
     172  	return 0;
     173  }
     174  
     175  /**
     176   * scols_column_set_json_type:
     177   * @cl: a pointer to a struct libscols_column instance
     178   * @type: SCOLS_JSON_* type
     179   *
     180   * Sets the type used for JSON formatting, the default is SCOLS_JSON_STRING.
     181   *
     182   * Returns: 0, a negative value in case of an error.
     183   *
     184   * Since: 2.33
     185   */
     186  int scols_column_set_json_type(struct libscols_column *cl, int type)
     187  {
     188  	if (!cl)
     189  		return -EINVAL;
     190  
     191  	cl->json_type = type;
     192  	return 0;
     193  
     194  }
     195  
     196  /**
     197   * scols_column_get_json_type:
     198   * @cl: a pointer to a struct libscols_column instance
     199   *
     200   * Note that SCOLS_JSON_BOOLEAN interprets NULL, empty strings, '0', 'N' and
     201   * 'n' as "false"; and everything else as "true".
     202   *
     203   * Returns: JSON type used for formatting or a negative value in case of an error.
     204   *
     205   * Since: 2.33
     206   */
     207  int scols_column_get_json_type(const struct libscols_column *cl)
     208  {
     209  	return cl ? cl->json_type : -EINVAL;
     210  }
     211  
     212  
     213  /**
     214   * scols_column_get_table:
     215   * @cl: a pointer to a struct libscols_column instance
     216   *
     217   * Returns: pointer to the table where columns is used
     218   */
     219  struct libscols_table *scols_column_get_table(const struct libscols_column *cl)
     220  {
     221  	return cl->table;
     222  }
     223  
     224  /**
     225   * scols_column_get_flags:
     226   * @cl: a pointer to a struct libscols_column instance
     227   *
     228   * Returns: The flag mask of @cl, a negative value in case of an error.
     229   */
     230  int scols_column_get_flags(const struct libscols_column *cl)
     231  {
     232  	return cl->flags;
     233  }
     234  
     235  /**
     236   * scols_column_get_header:
     237   * @cl: a pointer to a struct libscols_column instance
     238   *
     239   * Returns: A pointer to a struct libscols_cell instance, representing the
     240   * header info of column @cl or NULL in case of an error.
     241   */
     242  struct libscols_cell *scols_column_get_header(struct libscols_column *cl)
     243  {
     244  	return &cl->header;
     245  }
     246  
     247  /**
     248   * scols_column_set_name:
     249   * @cl: a pointer to a struct libscols_column instance
     250   * @name: column name
     251   *
     252   * Returns: 0, a negative value in case of an error.
     253   *
     254   * Since: 2.38
     255   */
     256  int scols_column_set_name(struct libscols_column *cl, const char *name)
     257  {
     258  	struct libscols_cell *hr = scols_column_get_header(cl);
     259  
     260  	if (!hr)
     261  		return -EINVAL;
     262  
     263  	free(cl->shellvar);
     264  	cl->shellvar = NULL;
     265  
     266  	return scols_cell_set_data(hr, name);
     267  }
     268  
     269  /**
     270   * scols_column_get_name:
     271   * @cl: a pointer to a struct libscols_column instance
     272   *
     273   * Returns: A pointer to a column name, which is stored in column header
     274   *
     275   * Since: 2.38
     276   */
     277  const char *scols_column_get_name(struct libscols_column *cl)
     278  {
     279  	return scols_cell_get_data(&cl->header);
     280  }
     281  
     282  /**
     283   * scols_column_get_name_as_shellvar
     284   * @cl: a pointer to a struct libscols_column instance
     285   *
     286   * Like scols_column_get_name(), but column name is modified to be compatible with shells
     287   * requirements for variable names.
     288   *
     289   * Since: 2.38
     290   */
     291  const char *scols_column_get_name_as_shellvar(struct libscols_column *cl)
     292  {
     293  	if (!cl->shellvar) {
     294  		const char *s, *name = scols_column_get_name(cl);
     295  		char *p;
     296  		size_t sz;
     297  
     298  		if (!name || !*name)
     299  			return NULL;
     300  
     301  		/* "1FOO%" --> "_1FOO_PCT */
     302  		sz = strlen(name) + 1 + 3;
     303  		p = cl->shellvar = calloc(1, sz + 1);
     304  		if (!cl->shellvar)
     305  			return NULL;
     306  
     307  		 /* convert "1FOO" to "_1FOO" */
     308  		if (!isalpha(*name))
     309  			*p++ = '_';
     310  
     311  		/* replace all "bad" chars with "_" */
     312  		for (s = name; *s; s++)
     313  			*p++ = !isalnum(*s) ? '_' : *s;
     314  
     315  		if (!*s && *(s - 1) == '%') {
     316  			*p++ = 'P';
     317  			*p++ = 'C';
     318  			*p++ = 'T';
     319  		}
     320  	}
     321  	return cl->shellvar;
     322  }
     323  
     324  
     325  /**
     326   * scols_column_set_color:
     327   * @cl: a pointer to a struct libscols_column instance
     328   * @color: color name or ESC sequence
     329   *
     330   * The default color for data cells and column header.
     331   *
     332   * If you want to set header specific color then use scols_column_get_header()
     333   * and scols_cell_set_color().
     334   *
     335   * If you want to set data cell specific color the use scols_line_get_cell() +
     336   * scols_cell_set_color().
     337   *
     338   * Returns: 0, a negative value in case of an error.
     339   */
     340  int scols_column_set_color(struct libscols_column *cl, const char *color)
     341  {
     342  	if (color && !color_is_sequence(color)) {
     343  		char *seq = color_get_sequence(color);
     344  		if (!seq)
     345  			return -EINVAL;
     346  		free(cl->color);
     347  		cl->color = seq;
     348  		return 0;
     349  	}
     350  	return strdup_to_struct_member(cl, color, color);
     351  }
     352  
     353  /**
     354   * scols_column_get_color:
     355   * @cl: a pointer to a struct libscols_column instance
     356   *
     357   * Returns: The current color setting of the column @cl.
     358   */
     359  const char *scols_column_get_color(const struct libscols_column *cl)
     360  {
     361  	return cl->color;
     362  }
     363  
     364  /**
     365   * scols_wrapnl_nextchunk:
     366   * @cl: a pointer to a struct libscols_column instance
     367   * @data: string
     368   * @userdata: callback private data
     369   *
     370   * This is built-in function for scols_column_set_wrapfunc(). This function
     371   * terminates the current chunk by \0 and returns pointer to the begin of
     372   * the next chunk. The chunks are based on \n.
     373   *
     374   * For example for data "AAA\nBBB\nCCC" the next chunk is "BBB".
     375   *
     376   * Returns: next chunk
     377   *
     378   * Since: 2.29
     379   */
     380  char *scols_wrapnl_nextchunk(const struct libscols_column *cl __attribute__((unused)),
     381  			char *data,
     382  			void *userdata __attribute__((unused)))
     383  {
     384  	char *p = data ? strchr(data, '\n') : NULL;
     385  
     386  	if (p) {
     387  		*p = '\0';
     388  		return p + 1;
     389  	}
     390  	return NULL;
     391  }
     392  
     393  /**
     394   * scols_wrapnl_chunksize:
     395   * @cl: a pointer to a struct libscols_column instance
     396   * @data: string
     397   * @userdata: callback private data
     398   *
     399   * Analyzes @data and returns size of the largest chunk. The chunks are based
     400   * on \n. For example for data "AAA\nBBB\nCCCC" the largest chunk size is 4.
     401   *
     402   * Note that the size has to be based on number of terminal cells rather than
     403   * bytes to support multu-byte output.
     404   *
     405   * Returns: size of the largest chunk.
     406   *
     407   * Since: 2.29
     408   */
     409  size_t scols_wrapnl_chunksize(const struct libscols_column *cl __attribute__((unused)),
     410  		const char *data,
     411  		void *userdata __attribute__((unused)))
     412  {
     413  	size_t sum = 0;
     414  
     415  	while (data && *data) {
     416  		const char *p;
     417  		size_t sz;
     418  
     419  		p = strchr(data, '\n');
     420  		if (p) {
     421  			sz = cl->table && scols_table_is_noencoding(cl->table) ?
     422  					mbs_nwidth(data, p - data) :
     423  					mbs_safe_nwidth(data, p - data, NULL);
     424  			p++;
     425  		} else {
     426  			sz = cl->table && scols_table_is_noencoding(cl->table) ?
     427  					mbs_width(data) :
     428  					mbs_safe_width(data);
     429  		}
     430  		sum = max(sum, sz);
     431  		data = p;
     432  	}
     433  
     434  	return sum;
     435  }
     436  
     437  /**
     438   * scols_column_set_cmpfunc:
     439   * @cl: column
     440   * @cmp: pointer to compare function
     441   * @data: private data for cmp function
     442   *
     443   * Returns: 0, a negative value in case of an error.
     444   */
     445  int scols_column_set_cmpfunc(struct libscols_column *cl,
     446  			int (*cmp)(struct libscols_cell *,
     447  				   struct libscols_cell *,
     448  				   void *),
     449  			void *data)
     450  {
     451  	if (!cl)
     452  		return -EINVAL;
     453  
     454  	cl->cmpfunc = cmp;
     455  	cl->cmpfunc_data = data;
     456  	return 0;
     457  }
     458  
     459  /**
     460   * scols_column_set_wrapfunc:
     461   * @cl: a pointer to a struct libscols_column instance
     462   * @wrap_chunksize: function to return size of the largest chink of data
     463   * @wrap_nextchunk: function to return next zero terminated data
     464   * @userdata: optional stuff for callbacks
     465   *
     466   * Extends SCOLS_FL_WRAP and can be used to set custom wrap function. The default
     467   * is to wrap by column size, but you can create functions to wrap for example
     468   * after \n or after words, etc.
     469   *
     470   * Returns: 0, a negative value in case of an error.
     471   *
     472   * Since: 2.29
     473   */
     474  int scols_column_set_wrapfunc(struct libscols_column *cl,
     475  			size_t (*wrap_chunksize)(const struct libscols_column *,
     476  						 const char *,
     477  						 void *),
     478  			char * (*wrap_nextchunk)(const struct libscols_column *,
     479  						 char *,
     480  						 void *),
     481  			void *userdata)
     482  {
     483  	if (!cl)
     484  		return -EINVAL;
     485  
     486  	cl->wrap_nextchunk = wrap_nextchunk;
     487  	cl->wrap_chunksize = wrap_chunksize;
     488  	cl->wrapfunc_data = userdata;
     489  	return 0;
     490  }
     491  
     492  /**
     493   * scols_column_set_safechars:
     494   * @cl: a pointer to a struct libscols_column instance
     495   * @safe: safe characters (e.g. "\n\t")
     496   *
     497   * Use for bytes you don't want to encode on output. This is for example
     498   * necessary if you want to use custom wrap function based on \n, in this case
     499   * you have to set "\n" as a safe char.
     500   *
     501   * Returns: 0, a negative value in case of an error.
     502   *
     503   * Since: 2.29
     504   */
     505  int scols_column_set_safechars(struct libscols_column *cl, const char *safe)
     506  {
     507  	return strdup_to_struct_member(cl, safechars, safe);
     508  }
     509  
     510  /**
     511   * scols_column_get_safechars:
     512   * @cl: a pointer to a struct libscols_column instance
     513   *
     514   * Returns: safe chars
     515   *
     516   * Since: 2.29
     517   */
     518  const char *scols_column_get_safechars(const struct libscols_column *cl)
     519  {
     520  	return cl->safechars;
     521  }
     522  
     523  /**
     524   * scols_column_get_width:
     525   * @cl: a pointer to a struct libscols_column instance
     526   *
     527   * Important note: the column width is unknown until library starts printing
     528   * (width is calculated before printing). The function is usable for example in
     529   * nextchunk() callback specified by scols_column_set_wrapfunc().
     530   *
     531   * See also scols_column_get_whint(), it returns wanted size (!= final size).
     532   *
     533   * Returns: column width
     534   *
     535   * Since: 2.29
     536   */
     537  size_t scols_column_get_width(const struct libscols_column *cl)
     538  {
     539  	return cl->width;
     540  }
     541  
     542  /**
     543   * scols_column_is_hidden:
     544   * @cl: a pointer to a struct libscols_column instance
     545   *
     546   * Gets the value of @cl's flag hidden.
     547   *
     548   * Returns: 0 or 1
     549   *
     550   * Since: 2.27
     551   */
     552  int scols_column_is_hidden(const struct libscols_column *cl)
     553  {
     554  	return cl->flags & SCOLS_FL_HIDDEN ? 1 : 0;
     555  }
     556  
     557  /**
     558   * scols_column_is_trunc:
     559   * @cl: a pointer to a struct libscols_column instance
     560   *
     561   * Gets the value of @cl's flag trunc.
     562   *
     563   * Returns: 0 or 1
     564   */
     565  int scols_column_is_trunc(const struct libscols_column *cl)
     566  {
     567  	return cl->flags & SCOLS_FL_TRUNC ? 1 : 0;
     568  }
     569  /**
     570   * scols_column_is_tree:
     571   * @cl: a pointer to a struct libscols_column instance
     572   *
     573   * Gets the value of @cl's flag tree.
     574   *
     575   * Returns: 0 or 1
     576   */
     577  int scols_column_is_tree(const struct libscols_column *cl)
     578  {
     579  	return cl->flags & SCOLS_FL_TREE ? 1 : 0;
     580  }
     581  /**
     582   * scols_column_is_right:
     583   * @cl: a pointer to a struct libscols_column instance
     584   *
     585   * Gets the value of @cl's flag right.
     586   *
     587   * Returns: 0 or 1
     588   */
     589  int scols_column_is_right(const struct libscols_column *cl)
     590  {
     591  	return cl->flags & SCOLS_FL_RIGHT ? 1 : 0;
     592  }
     593  /**
     594   * scols_column_is_strict_width:
     595   * @cl: a pointer to a struct libscols_column instance
     596   *
     597   * Gets the value of @cl's flag strict_width.
     598   *
     599   * Returns: 0 or 1
     600   */
     601  int scols_column_is_strict_width(const struct libscols_column *cl)
     602  {
     603  	return cl->flags & SCOLS_FL_STRICTWIDTH ? 1 : 0;
     604  }
     605  /**
     606   * scols_column_is_noextremes:
     607   * @cl: a pointer to a struct libscols_column instance
     608   *
     609   * Gets the value of @cl's flag no_extremes.
     610   *
     611   * Returns: 0 or 1
     612   */
     613  int scols_column_is_noextremes(const struct libscols_column *cl)
     614  {
     615  	return cl->flags & SCOLS_FL_NOEXTREMES ? 1 : 0;
     616  }
     617  /**
     618   * scols_column_is_wrap:
     619   * @cl: a pointer to a struct libscols_column instance
     620   *
     621   * Gets the value of @cl's flag wrap.
     622   *
     623   * Returns: 0 or 1
     624   *
     625   * Since: 2.28
     626   */
     627  int scols_column_is_wrap(const struct libscols_column *cl)
     628  {
     629  	return cl->flags & SCOLS_FL_WRAP ? 1 : 0;
     630  }
     631  /**
     632   * scols_column_is_customwrap:
     633   * @cl: a pointer to a struct libscols_column instance
     634   *
     635   * Returns: 0 or 1
     636   *
     637   * Since: 2.29
     638   */
     639  int scols_column_is_customwrap(const struct libscols_column *cl)
     640  {
     641  	return (cl->flags & SCOLS_FL_WRAP)
     642  		&& cl->wrap_chunksize
     643  		&& cl->wrap_nextchunk ? 1 : 0;
     644  }
     645  
     646  /**
     647   * scols_column_set_properties:
     648   * @cl: a pointer to a struct libscols_column instance
     649   * @opts: options string
     650   *
     651   * Set properties from string, the string is comma seprated list, like
     652   * "trunc,right,json=number", ...
     653   *
     654   * Returns: 0 on success, <0 on error
     655   *
     656   * Since: 2.39
     657   */
     658  int scols_column_set_properties(struct libscols_column *cl, const char *opts)
     659  {
     660  	char *str = (char *) opts;
     661  	char *name, *value;
     662  	size_t namesz, valuesz;
     663  	unsigned int flags = 0;
     664  	int rc = 0;
     665  
     666  	DBG(COL, ul_debugobj(cl, "apply properties '%s'", opts));
     667  
     668  	while (rc == 0
     669  	       && !ul_optstr_next(&str, &name, &namesz, &value, &valuesz)) {
     670  
     671  		if (strncmp(name, "trunc", namesz) == 0)
     672  			flags |= SCOLS_FL_TRUNC;
     673  
     674  		else if (strncmp(name, "tree", namesz) == 0)
     675  			flags |= SCOLS_FL_TREE;
     676  
     677  		else if (strncmp(name, "right", namesz) == 0)
     678  			flags |= SCOLS_FL_RIGHT;
     679  
     680  		else if (strncmp(name, "strictwidth", namesz) == 0)
     681  			flags |= SCOLS_FL_STRICTWIDTH;
     682  
     683  		else if (strncmp(name, "noextremes", namesz) == 0)
     684  			flags |= SCOLS_FL_STRICTWIDTH;
     685  
     686  		else if (strncmp(name, "hidden", namesz) == 0)
     687  			flags |= SCOLS_FL_HIDDEN;
     688  
     689  		else if (strncmp(name, "wrap", namesz) == 0)
     690  			flags |= SCOLS_FL_WRAP;
     691  
     692  		else if (value && strncmp(name, "json", namesz) == 0) {
     693  
     694  			if (strncmp(value, "string", valuesz) == 0)
     695  				rc = scols_column_set_json_type(cl, SCOLS_JSON_STRING);
     696  			else if (strncmp(value, "number", valuesz) == 0)
     697  				rc = scols_column_set_json_type(cl, SCOLS_JSON_NUMBER);
     698  			else if (strncmp(value, "array-string", valuesz) == 0)
     699  				rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_STRING);
     700  			else if (strncmp(value, "array-number", valuesz) == 0)
     701  				rc = scols_column_set_json_type(cl, SCOLS_JSON_ARRAY_NUMBER);
     702  			else if (strncmp(value, "boolean", valuesz) == 0)
     703  				rc = scols_column_set_json_type(cl, SCOLS_JSON_BOOLEAN);
     704  
     705  		} else if (value && strncmp(name, "width", namesz) == 0) {
     706  
     707  			char *end = NULL;
     708  			double x = strtod(value, &end);
     709  			if (errno || str == end)
     710  				return -EINVAL;
     711  
     712  			rc = scols_column_set_whint(cl, x);
     713  
     714  		} else if (value && strncmp(name, "color", namesz) == 0) {
     715  
     716  			char *x = strndup(value, valuesz);
     717  			if (x) {
     718  				scols_column_set_color(cl, x);
     719  				free(x);
     720  			}
     721  
     722  		} else if (value && strncmp(name, "name", namesz) == 0) {
     723  
     724  			char *x = strndup(value, valuesz);
     725  			if (x) {
     726  				scols_column_set_name(cl, x);
     727  				free(x);
     728  			}
     729  		}
     730  	}
     731  
     732  	if (!rc && flags)
     733  		rc = scols_column_set_flags(cl, flags);
     734  
     735  	return rc;
     736  }
     737