Redesign the character re-encoding code.
[pspp] / src / data / short-names.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007 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/hash.h>
27 #include <libpspp/message.h>
28 #include <libpspp/str.h>
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32
33 /* Compares two strings. */
34 static int
35 compare_strings (const void *a, const void *b, const void *aux UNUSED)
36 {
37   return strcmp (a, b);
38 }
39
40 /* Hashes a string. */
41 static unsigned
42 hash_string (const void *s, const void *aux UNUSED)
43 {
44   return hsh_hash_string (s);
45 }
46
47 /* Sets V's short name to BASE, followed by a suffix of the form
48    _A, _B, _C, ..., _AA, _AB, etc. according to the value of
49    SUFFIX_NUMBER.  Truncates BASE as necessary to fit. */
50 static void
51 set_var_short_name_suffix (struct variable *v, size_t i,
52                            const char *base, int suffix_number)
53 {
54   char suffix[SHORT_NAME_LEN + 1];
55   char short_name[SHORT_NAME_LEN + 1];
56   int len, ofs;
57
58   assert (suffix_number >= 0);
59
60   /* Set base name. */
61   var_set_short_name (v, i, base);
62
63   /* Compose suffix. */
64   suffix[0] = '_';
65   if (!str_format_26adic (suffix_number, &suffix[1], sizeof suffix - 1))
66     msg (SE, _("Variable suffix too large."));
67   len = strlen (suffix);
68
69   /* Append suffix to V's short name. */
70   str_copy_trunc (short_name, sizeof short_name, base);
71   if (strlen (short_name) + len > SHORT_NAME_LEN)
72     ofs = SHORT_NAME_LEN - len;
73   else
74     ofs = strlen (short_name);
75   strcpy (short_name + ofs, suffix);
76
77   /* Set name. */
78   var_set_short_name (v, i, short_name);
79 }
80
81 static void
82 claim_short_name (struct variable *v, size_t i, struct hsh_table *short_names)
83 {
84   const char *short_name = var_get_short_name (v, i);
85   if (short_name != NULL
86       && hsh_insert (short_names, (char *) short_name) != NULL)
87     var_set_short_name (v, i, NULL);
88 }
89
90 /* Form initial short_name from the variable name, then try _A,
91    _B, ... _AA, _AB, etc., if needed. */
92 static void
93 assign_short_name (struct variable *v, size_t i, struct hsh_table *short_names)
94 {
95   int trial;
96
97   if (var_get_short_name (v, i) != NULL)
98     return;
99
100   for (trial = 0; ; trial++)
101     {
102       if (trial == 0)
103         var_set_short_name (v, i, var_get_name (v));
104       else
105         set_var_short_name_suffix (v, i, var_get_name (v), trial);
106
107       if (hsh_insert (short_names, (char *) var_get_short_name (v, i)) == NULL)
108         break;
109     }
110 }
111
112 /* Assigns a valid, unique short_name[] to each variable in D.
113    Each variable whose actual name is short has highest priority
114    for that short name.  Otherwise, variables with an existing
115    short_name[] have the next highest priority for a given short
116    name; if it is already taken, then the variable is treated as
117    if short_name[] had been empty.  Otherwise, long names are
118    truncated to form short names.  If that causes conflicts,
119    variables are renamed as PREFIX_A, PREFIX_B, and so on. */
120 void
121 short_names_assign (struct dictionary *d)
122 {
123   size_t var_cnt = dict_get_var_cnt (d);
124   struct hsh_table *short_names;
125   size_t i, j;
126
127   /* Create hash used for detecting conflicts.  The entries in
128      the hash table point to strings owned by dictionary
129      variables, not by us, so we don't need to provide a free
130      function. */
131   short_names = hsh_create (var_cnt, compare_strings, hash_string,
132                             NULL, NULL);
133
134   /* Clear short names that conflict with a variable name. */
135   for (i = 0; i < var_cnt; i++)
136     {
137       struct variable *v = dict_get_var (d, i);
138       int segment_cnt = sfm_width_to_segments (var_get_width (v));
139       for (j = 0; j < segment_cnt; j++)
140         {
141           const char *name = var_get_short_name (v, j);
142           if (name != NULL)
143             {
144               struct variable *ov = dict_lookup_var (d, name);
145               if (ov != NULL && (ov != v || j > 0))
146                 var_set_short_name (v, j, NULL);
147             }
148         }
149     }
150
151   /* Give variables whose names are short the corresponding short
152      name. */
153   for (i = 0; i < var_cnt; i++)
154     {
155       struct variable *v = dict_get_var (d, i);
156       if (strlen (var_get_name (v)) <= SHORT_NAME_LEN)
157         var_set_short_name (v, 0, var_get_name (v));
158     }
159
160   /* Each variable with an assigned short name for its first
161      segment now gets it unless there is a conflict.  In case of
162      conflict, the claimant earlier in dictionary order wins.
163      Then similarly for additional segments of very long
164      strings. */
165   for (i = 0; i < var_cnt; i++)
166     {
167       struct variable *v = dict_get_var (d, i);
168       claim_short_name (v, 0, short_names);
169     }
170   for (i = 0; i < var_cnt; i++)
171     {
172       struct variable *v = dict_get_var (d, i);
173       int segment_cnt = sfm_width_to_segments (var_get_width (v));
174       for (j = 1; j < segment_cnt; j++)
175         claim_short_name (v, j, short_names);
176     }
177
178   /* Assign short names to first segment of remaining variables,
179      then similarly for additional segments. */
180   for (i = 0; i < var_cnt; i++)
181     {
182       struct variable *v = dict_get_var (d, i);
183       assign_short_name (v, 0, short_names);
184     }
185   for (i = 0; i < var_cnt; i++)
186     {
187       struct variable *v = dict_get_var (d, i);
188       int segment_cnt = sfm_width_to_segments (var_get_width (v));
189       for (j = 1; j < segment_cnt; j++)
190         assign_short_name (v, j, short_names);
191     }
192
193   /* Get rid of hash table. */
194   hsh_destroy (short_names);
195 }