d1e37f5be06e079c846383dbbd16e70bb360e39f
[pspp-builds.git] / src / data / short-names.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "data/short-names.h"
20
21 #include "data/dictionary.h"
22 #include "data/sys-file-private.h"
23 #include "data/variable.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/compiler.h"
26 #include "libpspp/message.h"
27 #include "libpspp/str.h"
28 #include "libpspp/stringi-set.h"
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32
33 /* Sets V's short name to BASE, followed by a suffix of the form
34    _A, _B, _C, ..., _AA, _AB, etc. according to the value of
35    SUFFIX_NUMBER.  Truncates BASE as necessary to fit. */
36 static void
37 set_var_short_name_suffix (struct variable *v, size_t i,
38                            const char *base, int suffix_number)
39 {
40   char suffix[SHORT_NAME_LEN + 1];
41   char short_name[SHORT_NAME_LEN + 1];
42   int len, ofs;
43
44   assert (suffix_number >= 0);
45
46   /* Set base name. */
47   var_set_short_name (v, i, base);
48
49   /* Compose suffix. */
50   suffix[0] = '_';
51   if (!str_format_26adic (suffix_number, &suffix[1], sizeof suffix - 1))
52     msg (SE, _("Variable suffix too large."));
53   len = strlen (suffix);
54
55   /* Append suffix to V's short name. */
56   str_copy_trunc (short_name, sizeof short_name, base);
57   if (strlen (short_name) + len > SHORT_NAME_LEN)
58     ofs = SHORT_NAME_LEN - len;
59   else
60     ofs = strlen (short_name);
61   strcpy (short_name + ofs, suffix);
62
63   /* Set name. */
64   var_set_short_name (v, i, short_name);
65 }
66
67 static void
68 claim_short_name (struct variable *v, size_t i,
69                   struct stringi_set *short_names)
70 {
71   const char *short_name = var_get_short_name (v, i);
72   if (short_name != NULL && !stringi_set_insert (short_names, short_name))
73     var_set_short_name (v, i, NULL);
74 }
75
76 /* Form initial short_name from the variable name, then try _A,
77    _B, ... _AA, _AB, etc., if needed. */
78 static void
79 assign_short_name (struct variable *v, size_t i,
80                    struct stringi_set *short_names)
81 {
82   int trial;
83
84   if (var_get_short_name (v, i) != NULL)
85     return;
86
87   for (trial = 0; ; trial++)
88     {
89       if (trial == 0)
90         var_set_short_name (v, i, var_get_name (v));
91       else
92         set_var_short_name_suffix (v, i, var_get_name (v), trial);
93
94       if (stringi_set_insert (short_names, var_get_short_name (v, i)))
95         break;
96     }
97 }
98
99 /* Assigns a valid, unique short_name[] to each variable in D.
100    Each variable whose actual name is short has highest priority
101    for that short name.  Otherwise, variables with an existing
102    short_name[] have the next highest priority for a given short
103    name; if it is already taken, then the variable is treated as
104    if short_name[] had been empty.  Otherwise, long names are
105    truncated to form short names.  If that causes conflicts,
106    variables are renamed as PREFIX_A, PREFIX_B, and so on. */
107 void
108 short_names_assign (struct dictionary *d)
109 {
110   size_t var_cnt = dict_get_var_cnt (d);
111   struct stringi_set short_names;
112   size_t i, j;
113
114   stringi_set_init (&short_names);
115
116   /* Clear short names that conflict with a variable name. */
117   for (i = 0; i < var_cnt; i++)
118     {
119       struct variable *v = dict_get_var (d, i);
120       int segment_cnt = sfm_width_to_segments (var_get_width (v));
121       for (j = 0; j < segment_cnt; j++)
122         {
123           const char *name = var_get_short_name (v, j);
124           if (name != NULL)
125             {
126               struct variable *ov = dict_lookup_var (d, name);
127               if (ov != NULL && (ov != v || j > 0))
128                 var_set_short_name (v, j, NULL);
129             }
130         }
131     }
132
133   /* Give variables whose names are short the corresponding short
134      name. */
135   for (i = 0; i < var_cnt; i++)
136     {
137       struct variable *v = dict_get_var (d, i);
138       const char *name = var_get_name (v);
139       if (strlen (name) <= SHORT_NAME_LEN)
140         var_set_short_name (v, 0, name);
141     }
142
143   /* Each variable with an assigned short name for its first
144      segment now gets it unless there is a conflict.  In case of
145      conflict, the claimant earlier in dictionary order wins.
146      Then similarly for additional segments of very long
147      strings. */
148   for (i = 0; i < var_cnt; i++)
149     {
150       struct variable *v = dict_get_var (d, i);
151       claim_short_name (v, 0, &short_names);
152     }
153   for (i = 0; i < var_cnt; i++)
154     {
155       struct variable *v = dict_get_var (d, i);
156       int segment_cnt = sfm_width_to_segments (var_get_width (v));
157       for (j = 1; j < segment_cnt; j++)
158         claim_short_name (v, j, &short_names);
159     }
160
161   /* Assign short names to first segment of remaining variables,
162      then similarly for additional segments. */
163   for (i = 0; i < var_cnt; i++)
164     {
165       struct variable *v = dict_get_var (d, i);
166       assign_short_name (v, 0, &short_names);
167     }
168   for (i = 0; i < var_cnt; i++)
169     {
170       struct variable *v = dict_get_var (d, i);
171       int segment_cnt = sfm_width_to_segments (var_get_width (v));
172       for (j = 1; j < segment_cnt; j++)
173         assign_short_name (v, j, &short_names);
174     }
175
176   stringi_set_destroy (&short_names);
177 }