BRL-CAD
lc.c
Go to the documentation of this file.
1 /* L C . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2014-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @file libged/lc.c
21  *
22  * The lc command.
23  *
24  */
25 
26 #include "common.h"
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <errno.h>
31 
32 #include "ged.h"
33 #include "bu/str.h"
34 #include "bu/vls.h"
35 #include "bu/file.h"
36 #include "bu/getopt.h"
37 #include "bu/sort.h"
38 #include "bu/ptbl.h"
39 
40 
42 {
43  int ignore;
44  const char *region_id;
45  const char *material_id;
46  const char *los;
47  const char *obj_name;
48  const char *obj_parent;
49 };
50 
51 /**
52  * Sorts the region lists such that identical entries are next to each
53  * other; used to remove duplicates.
54  */
55 static int
56 cmp_regions(const void *a, const void *b, void *UNUSED(arg))
57 {
58  struct region_record *r1 = (struct region_record *)a;
59  struct region_record *r2 = (struct region_record *)b;
60  int cmp;
61 
62  cmp = bu_strcmp(r1->region_id, r2->region_id);
63  if (cmp) { return cmp; }
64  cmp = bu_strcmp(r1->material_id, r2->material_id);
65  if (cmp) { return cmp; }
66  cmp = bu_strcmp(r1->los, r2->los);
67  return cmp;
68 }
69 
70 /**
71  * Sorts the region list according to the sort parameter (1 - 5) passed in.
72  */
73 static int
74 sort_regions(const void *a, const void *b, void *arg)
75 {
76  struct region_record *r1 = (struct region_record *)a;
77  struct region_record *r2 = (struct region_record *)b;
78  int *sort_type = (int *)arg;
79 
80  switch (*sort_type) {
81  case 1:
82  return bu_strcmp(r1->region_id, r2->region_id);
83  case 2:
84  return bu_strcmp(r1->material_id, r2->material_id);
85  case 3:
86  return bu_strcmp(r1->los, r2->los);
87  case 4:
88  return bu_strcmp(r1->obj_name, r2->obj_name);
89  case 5:
90  return bu_strcmp(r1->obj_parent, r2->obj_parent);
91  }
92  /* This should never be executed */
93  return 0;
94 }
95 
96 static void
97 print_cmd_args(struct bu_vls *output, int argc, const char *argv[])
98 {
99  int i;
100  bu_vls_printf(output, "Command args: '");
101  for (i = 0; i < argc; i++) {
102  bu_vls_printf(output, "%s", argv[i]);
103  if (i < argc - 1) {
104  bu_vls_printf(output, " ");
105  } else {
106  bu_vls_printf(output, "'\n");
107  }
108  }
109 }
110 
111 /**
112  * Get an attribute from a bu_avs, like bu_avs_get, but return "--" if
113  * the item is missing, instead of NULL
114  */
115 static const char *
116 get_attr(const struct bu_attribute_value_set *avp, const char *attribute)
117 {
118  const char *value = bu_avs_get(avp, attribute);
119  return value ? value : "--";
120 }
121 
122 
123 int
124 ged_lc(struct ged *gedp, int argc, const char *argv[])
125 {
126  char *file_name = NULL;
127  int file_name_flag_cnt = 0;
128  int sort_column = 1;
129  int sort_column_flag_cnt = 0;
130  int find_duplicates_flag = 0;
131  int skip_special_duplicates_flag = 0;
132  int skip_subtracted_regions_flag = 0;
133  int descending_sort_flag = 0;
134  int unrecognized_flag_cnt = 0;
135 
136  int orig_argc;
137  const char **orig_argv;
138 
139  static const char *usage = "[-d|-s|-r] [-z] [-0|-1|-2|-3|-4|-5] [-f {FileName}] {GroupName}";
140 
141  int c;
142  int error_cnt = 0;
143 
144  FILE *outfile = NULL;
145  struct bu_vls *output;
146 
147  const char *group_name;
148 
149  size_t i = 0;
150  struct bu_ptbl results1 = BU_PTBL_INIT_ZERO;
151  struct bu_ptbl results2 = BU_PTBL_INIT_ZERO;
152  char *path;
153  char *plan;
154  struct db_full_path root;
155  int matches;
156  struct region_record *regions;
157  size_t ignored_cnt = 0;
158 
159  /* The defaults are the minimum widths to accommodate column headers */
160  size_t region_id_len_max = 2;
161  size_t material_id_len_max = 3;
162  size_t los_len_max = 3;
163  size_t obj_len_max = 6;
164 
165  /* For the output at the end */
166  size_t start, end, incr;
167 
168  /* initialize result */
169  bu_vls_trunc(gedp->ged_result_str, 0);
170 
171  if (argc == 1) {
172  bu_vls_printf(gedp->ged_result_str, "Usage: %s\n", usage);
173  return GED_HELP;
174  }
175 
177  GED_CHECK_ARGC_GT_0(gedp, argc, GED_ERROR);
178 
179  bu_optind = 1; /* re-init bu_getopt() */
180  while ((c = bu_getopt(argc, (char * const *)argv, "dsrz012345f:")) != -1) {
181  switch (c) {
182  case '0':
183  case '1':
184  case '2':
185  case '3':
186  case '4':
187  case '5':
188  sort_column_flag_cnt++;
189  sort_column = c - '0';
190  break;
191  case 'f':
192  file_name_flag_cnt++;
193  file_name = bu_optarg;
194  break;
195  case 'd':
196  find_duplicates_flag = 1;
197  break;
198  case 's':
199  find_duplicates_flag = 1;
200  skip_special_duplicates_flag = 1;
201  break;
202  case 'r':
203  skip_subtracted_regions_flag = 1;
204  break;
205  case 'z':
206  descending_sort_flag = 1;
207  default:
208  unrecognized_flag_cnt++;
209  }
210  }
211  orig_argc = argc;
212  orig_argv = argv;
213  argc -= (bu_optind - 1);
214  argv += (bu_optind - 1);
215 
216  /* Attempt to recreate the exact error messages from the original lc.tcl */
217 
218  if (file_name_flag_cnt > 1) {
219  bu_vls_printf(gedp->ged_result_str, "Error: '-f' used more than once.\n");
220  error_cnt++;
221  }
222 
223  if (sort_column_flag_cnt > 1) {
224  bu_vls_printf(gedp->ged_result_str, "Error: Sort column defined more than once.\n");
225  error_cnt++;
226  }
227 
228  if (file_name_flag_cnt + argc + unrecognized_flag_cnt > 3) {
229  bu_vls_printf(gedp->ged_result_str, "Error: More than one group name and/or file name was specified.\n");
230  error_cnt++;
231  } else if (argc < 2) {
232  if (file_name_flag_cnt && !file_name) {
233  bu_vls_printf(gedp->ged_result_str, "Error: Group name and file name not specified\n");
234  error_cnt++;
235  } else {
236  bu_vls_printf(gedp->ged_result_str, "Error: Group name not specified.\n");
237  error_cnt++;
238  }
239  } else if (argc + unrecognized_flag_cnt > 2) {
240  bu_vls_printf(gedp->ged_result_str, "Error: More than one group name was specified.\n");
241  error_cnt++;
242  } else if (file_name_flag_cnt && !file_name) {
243  bu_vls_printf(gedp->ged_result_str, "Error: File name not specified.\n");
244  error_cnt++;
245  }
246 
247  if (file_name) {
248  char *norm_name;
249  if (file_name[0] == '-') {
250  bu_vls_printf(gedp->ged_result_str, "Error: File name can not start with '-'.\n");
251  error_cnt++;
252  } else if (bu_file_exists(file_name, NULL)) {
253  bu_vls_printf(gedp->ged_result_str, "Error: File '$norm_name' already exists.\n");
254  error_cnt++;
255  } else {
256  outfile = fopen(file_name, "w");
257  if (!outfile) {
258  bu_vls_printf(gedp->ged_result_str, "Error: %d\n", errno);
259  error_cnt++;
260  }
261  }
262  norm_name = bu_realpath(file_name, NULL);
263  bu_vls_printf(gedp->ged_result_str, "Output filename: %s\n", norm_name);
264  bu_free(norm_name, "ged_lc");
265  output = bu_vls_vlsinit();
266  } else {
267  output = gedp->ged_result_str;
268  }
269 
270  if (error_cnt > 0) { return GED_ERROR; }
271 
272  group_name = argv[1];
273 
274  /* The 7 is for the "-name" and '\0' */
275  plan = (char *) bu_malloc(sizeof(char) * (strlen(group_name) + 7), "ged_lc");
276  sprintf(plan, "-name %s", group_name);
277  matches = db_search(&results1, DB_SEARCH_TREE, plan, 0, NULL, gedp->ged_wdbp->dbip);
278  if (matches < 1) {
279  bu_vls_printf(gedp->ged_result_str, "Error: Group '%s' does not exist.\n", group_name);
280  return GED_ERROR;
281  }
282  bu_free(plan, "ged_lc");
283  db_search_free(&results1);
284 
285  if (skip_subtracted_regions_flag) {
286  plan = "-type region ! -bool -";
287  } else {
288  plan = "-type region";
289  }
290  path = (char *) bu_malloc(sizeof(char) * (strlen(group_name) + 2), "ged_lc");
291  sprintf(path, "/%s", group_name);
292  db_string_to_path(&root, gedp->ged_wdbp->dbip, path);
293  matches = db_search(&results2, DB_SEARCH_TREE, plan, root.fp_len, root.fp_names, gedp->ged_wdbp->dbip);
294  bu_free(path, "ged_lc");
295  if (matches < 1) { return GED_ERROR; }
296  regions = (struct region_record *) bu_malloc(sizeof(struct region_record) * BU_PTBL_LEN(&results2), "ged_lc");
297  for (i = 0; i < BU_PTBL_LEN(&results2); i++) {
298  struct db_full_path *entry = (struct db_full_path *)BU_PTBL_GET(&results2, i);
299  struct directory *dp_curr_dir = DB_FULL_PATH_CUR_DIR(entry);
300  struct bu_attribute_value_set avs;
301 
302  regions[i].ignore = 0;
303 
304  bu_avs_init_empty(&avs);
305  db5_get_attributes(gedp->ged_wdbp->dbip, &avs, dp_curr_dir);
306 
307  regions[i].region_id = get_attr(&avs, "region_id");
308  V_MAX(region_id_len_max, strlen(regions[i].region_id));
309  regions[i].material_id = get_attr(&avs, "material_id");
310  V_MAX(material_id_len_max, strlen(regions[i].material_id));
311  regions[i].los = get_attr(&avs, "los");
312  V_MAX(los_len_max, strlen(regions[i].los));
313  regions[i].obj_name = dp_curr_dir->d_namep;
314  V_MAX(obj_len_max, strlen(regions[i].obj_name));
315 
316  if (entry->fp_len > 1) {
317  struct directory *dp_parent = DB_FULL_PATH_GET(entry, entry->fp_len - 2);
318  regions[i].obj_parent = dp_parent->d_namep;
319  } else {
320  regions[i].obj_parent = "--";
321  }
322  }
323 
324  if (find_duplicates_flag) {
325 
326  bu_sort((void *) regions, BU_PTBL_LEN(&results2), sizeof(struct region_record), cmp_regions, NULL);
327 
328  for (i = 1; i < BU_PTBL_LEN(&results2); i++) {
329  int same;
330  if (skip_special_duplicates_flag) {
331  same = !cmp_regions((void *)&(regions[i - 1]), (void *)&(regions[i]), NULL);
332  } else {
333  same = !bu_strcmp(regions[i - 1].region_id, regions[i].region_id);
334  }
335  if (same) {
336  regions[i].ignore = 1;
337  ignored_cnt++;
338  }
339  }
340 
341  if (ignored_cnt == BU_PTBL_LEN(&results2)) {
342  if (file_name) {
343  print_cmd_args(output, orig_argc, orig_argv);
344  bu_vls_printf(output, "No duplicate region_id\n");
345  bu_vls_fwrite(outfile, output);
346  fclose(outfile);
347  }
348  bu_vls_printf(gedp->ged_result_str, "No duplicate region_id\n");
349  bu_vls_printf(gedp->ged_result_str, "Done.\n");
350  bu_free(regions, "ged_lc");
351  return GED_ERROR;
352  }
353  }
354 
355  if (sort_column > 0) {
356  bu_sort((void *) regions, BU_PTBL_LEN(&results2), sizeof(struct region_record), sort_regions, (void *)&sort_column);
357  }
358 
359  if (file_name) {
360  print_cmd_args(output, orig_argc, orig_argv);
361  }
362 
363  bu_vls_printf(output, "List length: %d\n", BU_PTBL_LEN(&results2) - ignored_cnt);
364  bu_vls_printf(output, "%-*s %-*s %-*s %-*s %s\n",
365  region_id_len_max + 1, "ID",
366  material_id_len_max + 1, "MAT",
367  los_len_max, "LOS",
368  obj_len_max, "REGION",
369  "PARENT");
370  start = 0; end = BU_PTBL_LEN(&results2); incr = 1;
371  if (descending_sort_flag) {
372  start = end - 1; end = -1; incr = -1;
373  }
374  for (i = start; i != end; i += incr) {
375  if (regions[i].ignore) { continue; }
376  bu_vls_printf(output, "%-*s %-*s %-*s %-*s %s\n",
377  region_id_len_max + 1, regions[i].region_id,
378  material_id_len_max + 1, regions[i].material_id,
379  los_len_max, regions[i].los,
380  obj_len_max, regions[i].obj_name,
381  regions[i].obj_parent);
382  }
383  bu_vls_printf(gedp->ged_result_str, "Done.\n");
384 
385  if (file_name) {
386  bu_vls_fwrite(outfile, output);
387  fclose(outfile);
388  }
389 
390  bu_free(regions, "ged_lc");
391 
392  return GED_OK;
393 }
394 
395 /*
396  * Local Variables:
397  * tab-width: 8
398  * mode: C
399  * indent-tabs-mode: t
400  * c-file-style: "stroustrup"
401  * End:
402  * ex: shiftwidth=4 tabstop=8
403  */
void usage(struct ged *gedp)
Definition: coil.c:315
#define GED_OK
Definition: ged.h:55
char * d_namep
pointer to name string
Definition: raytrace.h:859
int ignore
Definition: lc.c:43
void bu_avs_init_empty(struct bu_attribute_value_set *avp)
Definition: avs.c:36
void db_search_free(struct bu_ptbl *search_results)
Definition: search.c:2089
#define BU_PTBL_INIT_ZERO
Definition: ptbl.h:92
size_t fp_len
Definition: db_fullpath.h:44
Definition: ged.h:338
struct db_i * dbip
Definition: raytrace.h:1266
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
#define GED_CHECK_ARGC_GT_0(_gedp, _argc, _flags)
Definition: ged.h:202
struct rt_wdb * ged_wdbp
Definition: ged.h:340
char * bu_optarg
Definition: globals.c:91
const char * los
Definition: lc.c:46
Header file for the BRL-CAD common definitions.
int bu_optind
Definition: globals.c:89
const char * region_id
Definition: lc.c:44
#define DB_FULL_PATH_CUR_DIR(_pp)
Definition: db_fullpath.h:51
int bu_getopt(int nargc, char *const nargv[], const char *ostr)
Definition: getopt.c:43
#define GED_ERROR
Definition: ged.h:61
#define DB_SEARCH_TREE
Do a hierarchy-aware search. This is the default.
Definition: search.h:110
void * bu_malloc(size_t siz, const char *str)
Definition: malloc.c:314
Definition: ptbl.h:62
const char * bu_avs_get(const struct bu_attribute_value_set *avp, const char *attribute)
Definition: avs.c:172
const char * material_id
Definition: lc.c:45
#define DB_FULL_PATH_GET(_pp, _i)
Definition: db_fullpath.h:55
#define GED_CHECK_DATABASE_OPEN(_gedp, _flags)
Definition: ged.h:114
int db_string_to_path(struct db_full_path *pp, const struct db_i *dbip, const char *str)
Definition: db_fullpath.c:361
#define BU_PTBL_GET(ptbl, i)
Definition: ptbl.h:108
const char * obj_name
Definition: lc.c:47
void bu_sort(void *array, size_t nummemb, size_t sizememb, int(*compare)(const void *, const void *, void *), void *context)
Definition: sort.c:110
void bu_vls_fwrite(FILE *fp, const struct bu_vls *vp)
Definition: vls.c:544
#define UNUSED(parameter)
Definition: common.h:239
struct bu_vls * bu_vls_vlsinit(void)
Definition: vls.c:91
struct directory ** fp_names
array of dir pointers
Definition: db_fullpath.h:46
struct bu_vls * ged_result_str
Definition: ged.h:357
#define BU_PTBL_LEN(ptbl)
Definition: ptbl.h:107
int bu_strcmp(const char *string1, const char *string2)
Definition: str.c:171
int ged_lc(struct ged *gedp, int argc, const char *argv[])
Definition: lc.c:124
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
#define GED_HELP
Definition: ged.h:62
void bu_free(void *ptr, const char *str)
Definition: malloc.c:328
char * file_name
Definition: fb2pix.c:40
char * bu_realpath(const char *path, char *resolved_path)
Definition: realpath.c:33
int db5_get_attributes(const struct db_i *dbip, struct bu_attribute_value_set *avs, const struct directory *dp)
Definition: db5_io.c:1027
Definition: vls.h:56
int bu_file_exists(const char *path, int *fd)
Definition: file.c:57
const char * obj_parent
Definition: lc.c:48
int db_search(struct bu_ptbl *results, int flags, const char *filter, int path_c, struct directory **path_v, struct db_i *dbip)
Search for objects in a geometry database using filters.
Definition: search.c:2232