Upload Tizen:Base source
[framework/base/util-linux-ng.git] / text-utils / column.c
1 /*
2  * Copyright (c) 1989, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33  
34 /*
35  * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
36  *      added Native Language Support
37  * 1999-09-19 Bruno Haible <haible@clisp.cons.org>
38  *      modified to work correctly in multi-byte locales
39  */
40
41 #include <sys/types.h>
42 #include <sys/ioctl.h>
43
44 #include <ctype.h>
45 #include <limits.h>
46 #include <stdio.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <err.h>
51 #include "nls.h"
52
53 #include "widechar.h"
54
55 #ifdef HAVE_WIDECHAR
56 #define wcs_width(s) wcswidth(s,wcslen(s))
57 static wchar_t *mbs_to_wcs(const char *);
58 #else
59 #define wcs_width(s) strlen(s)
60 #define mbs_to_wcs(s) strdup(s)
61 static char *mtsafe_strtok(char *, const char *, char **);
62 #define wcstok mtsafe_strtok
63 #endif
64
65 static void  c_columnate __P((void));
66 static void *emalloc __P((int));
67 static void  input __P((FILE *));
68 static void  maketbl __P((void));
69 static void  print __P((void));
70 static void  r_columnate __P((void));
71 static void  usage __P((void));
72
73 int termwidth = 80;             /* default terminal width */
74
75 int entries;                    /* number of records */
76 int eval;                       /* exit value */
77 int maxlength;                  /* longest record */
78 wchar_t **list;                 /* array of pointers to records */
79 wchar_t default_separator[] = { '\t', ' ', 0 };
80 wchar_t *separator = default_separator; /* field separator for table option */
81
82 int
83 main(int argc, char **argv)
84 {
85         struct winsize win;
86         FILE *fp;
87         int ch, tflag, xflag;
88         char *p;
89
90         extern char *__progname;
91         __progname = argv[0];
92
93         setlocale(LC_ALL, "");
94         bindtextdomain(PACKAGE, LOCALEDIR);
95         textdomain(PACKAGE);
96
97         if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) {
98                 if ((p = getenv("COLUMNS")) != NULL)
99                         termwidth = atoi(p);
100         } else
101                 termwidth = win.ws_col;
102
103         tflag = xflag = 0;
104         while ((ch = getopt(argc, argv, "c:s:tx")) != -1)
105                 switch(ch) {
106                 case 'c':
107                         termwidth = atoi(optarg);
108                         break;
109                 case 's':
110                         separator = mbs_to_wcs(optarg);
111                         break;
112                 case 't':
113                         tflag = 1;
114                         break;
115                 case 'x':
116                         xflag = 1;
117                         break;
118                 case '?':
119                 default:
120                         usage();
121                 }
122         argc -= optind;
123         argv += optind;
124
125         if (!*argv)
126                 input(stdin);
127         else for (; *argv; ++argv)
128                 if ((fp = fopen(*argv, "r")) != NULL) {
129                         input(fp);
130                         (void)fclose(fp);
131                 } else {
132                         warn("%s", *argv);
133                         eval = 1;
134                 }
135
136         if (!entries)
137                 exit(eval);
138
139         if (tflag)
140                 maketbl();
141         else if (maxlength >= termwidth)
142                 print();
143         else if (xflag)
144                 c_columnate();
145         else
146                 r_columnate();
147         if (ferror(stdout) || fclose(stdout))
148                 eval = 1;
149         exit(eval);
150 }
151
152 #define TAB     8
153 static void
154 c_columnate()
155 {
156         int chcnt, col, cnt, endcol, numcols;
157         wchar_t **lp;
158
159         maxlength = (maxlength + TAB) & ~(TAB - 1);
160         numcols = termwidth / maxlength;
161         endcol = maxlength;
162         for (chcnt = col = 0, lp = list;; ++lp) {
163                 fputws(*lp, stdout);
164                 chcnt += wcs_width(*lp);
165                 if (!--entries)
166                         break;
167                 if (++col == numcols) {
168                         chcnt = col = 0;
169                         endcol = maxlength;
170                         putwchar('\n');
171                 } else {
172                         while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
173                                 putwchar('\t');
174                                 chcnt = cnt;
175                         }
176                         endcol += maxlength;
177                 }
178         }
179         if (chcnt)
180                 putwchar('\n');
181 }
182
183 static void
184 r_columnate()
185 {
186         int base, chcnt, cnt, col, endcol, numcols, numrows, row;
187
188         maxlength = (maxlength + TAB) & ~(TAB - 1);
189         numcols = termwidth / maxlength;
190         if (!numcols) 
191           numcols = 1;
192         numrows = entries / numcols;
193         if (entries % numcols)
194                 ++numrows;
195
196         for (row = 0; row < numrows; ++row) {
197                 endcol = maxlength;
198                 for (base = row, chcnt = col = 0; col < numcols; ++col) {
199                         fputws(list[base], stdout);
200                         chcnt += wcs_width(list[base]);
201                         if ((base += numrows) >= entries)
202                                 break;
203                         while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) {
204                                 putwchar('\t');
205                                 chcnt = cnt;
206                         }
207                         endcol += maxlength;
208                 }
209                 putwchar('\n');
210         }
211 }
212
213 static void
214 print()
215 {
216         int cnt;
217         wchar_t **lp;
218
219         for (cnt = entries, lp = list; cnt--; ++lp) {
220                 fputws(*lp, stdout);
221                 putwchar('\n');
222         }
223 }
224
225 typedef struct _tbl {
226         wchar_t **list;
227         int cols, *len;
228 } TBL;
229 #define DEFCOLS 25
230
231 static void
232 maketbl()
233 {
234         TBL *t;
235         int coloff, cnt, i;
236         wchar_t *p, **lp;
237         int *lens, maxcols;
238         TBL *tbl;
239         wchar_t **cols;
240         wchar_t *wcstok_state;
241
242         t = tbl = emalloc(entries * sizeof(TBL));
243         cols = emalloc((maxcols = DEFCOLS) * sizeof(wchar_t *));
244         lens = emalloc(maxcols * sizeof(int));
245         for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) {
246             for (coloff = 0, p = *lp;
247                  (cols[coloff] = wcstok(p, separator, &wcstok_state)) != NULL;
248                  p = NULL)
249                 if (++coloff == maxcols) {
250                     if (!(cols = realloc(cols, ((u_int)maxcols + DEFCOLS)
251                                                * sizeof(wchar_t *))) ||
252                         !(lens = realloc(lens, ((u_int)maxcols + DEFCOLS)
253                                                * sizeof(int))))
254                         err(1, NULL);
255                     memset((char *)lens + maxcols * sizeof(int),
256                            0, DEFCOLS * sizeof(int));
257                     maxcols += DEFCOLS;
258                 }
259             t->list = emalloc(coloff * sizeof(wchar_t *));
260             t->len = emalloc(coloff * sizeof(int));
261             for (t->cols = coloff; --coloff >= 0;) {
262                 t->list[coloff] = cols[coloff];
263                 t->len[coloff] = wcs_width(cols[coloff]);
264                 if (t->len[coloff] > lens[coloff])
265                     lens[coloff] = t->len[coloff];
266             }
267         }
268         for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) {
269             for (coloff = 0; coloff < t->cols - 1; ++coloff) {
270                 fputws(t->list[coloff], stdout);
271                 for (i = lens[coloff] - t->len[coloff] + 2; i > 0; i--)
272                     putwchar(' ');
273             }
274             fputws(t->list[coloff], stdout);
275             putwchar('\n');
276         }
277 }
278
279 #define DEFNUM          1000
280 #define MAXLINELEN      (LINE_MAX + 1)
281
282 static void
283 input(fp)
284         FILE *fp;
285 {
286         static int maxentry;
287         int len;
288         wchar_t *p, buf[MAXLINELEN];
289
290         if (!list)
291                 list = emalloc((maxentry = DEFNUM) * sizeof(wchar_t *));
292         while (fgetws(buf, MAXLINELEN, fp)) {
293                 for (p = buf; *p && iswspace(*p); ++p);
294                 if (!*p)
295                         continue;
296                 if (!(p = wcschr(p, '\n'))) {
297                         warnx(_("line too long"));
298                         eval = 1;
299                         continue;
300                 }
301                 *p = '\0';
302                 len = wcs_width(buf);   /* len = p - buf; */
303                 if (maxlength < len)
304                         maxlength = len;
305                 if (entries == maxentry) {
306                         maxentry += DEFNUM;
307                         if (!(list = realloc(list,
308                             (u_int)maxentry * sizeof(wchar_t *))))
309                                 err(1, NULL);
310                 }
311                 list[entries++] = wcsdup(buf);
312         }
313 }
314
315 #ifdef HAVE_WIDECHAR
316 static wchar_t *mbs_to_wcs(const char *s)
317 {
318         size_t n;
319         wchar_t *wcs;
320
321         n = mbstowcs((wchar_t *)0, s, 0);
322         if (n < 0)
323                 return NULL;
324         wcs = malloc((n + 1) * sizeof(wchar_t));
325         if (!wcs)
326                 return NULL;
327         if (mbstowcs(wcs, s, n + 1) < 0)
328                 return NULL;
329         return wcs;
330 }
331 #endif
332
333 #ifndef HAVE_WIDECHAR
334 static char *mtsafe_strtok(char *str, const char *delim, char **ptr)
335 {
336         if (str == NULL) {
337                 str = *ptr;
338                 if (str == NULL)
339                         return NULL;
340         }
341         str += strspn(str, delim);
342         if (*str == '\0') {
343                 *ptr = NULL;
344                 return NULL;
345         } else {
346                 char *token_end = strpbrk(str, delim);
347                 if (token_end) {
348                         *token_end = '\0';
349                         *ptr = token_end + 1;
350                 } else
351                         *ptr = NULL;
352                 return str;
353         }
354 }
355 #endif
356
357 static void *
358 emalloc(size)
359         int size;
360 {
361         char *p;
362
363         if (!(p = malloc(size)))
364                 err(1, NULL);
365         memset(p, 0, size);
366         return (p);
367 }
368
369 static void
370 usage()
371 {
372
373         (void)fprintf(stderr,
374             _("usage: column [-tx] [-c columns] [file ...]\n"));
375         exit(1);
376 }