]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/fold/fold.c
ar(1): Fix grammar error in write.c
[FreeBSD/FreeBSD.git] / usr.bin / fold / fold.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1990, 1993
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kevin Ruddy.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <err.h>
36 #include <limits.h>
37 #include <locale.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <wchar.h>
43 #include <wctype.h>
44
45 #define DEFLINEWIDTH    80
46
47 void fold(int);
48 static int newpos(int, wint_t);
49 static void usage(void) __dead2;
50
51 static int bflag;               /* Count bytes, not columns */
52 static int sflag;               /* Split on word boundaries */
53
54 int
55 main(int argc, char **argv)
56 {
57         int ch, previous_ch;
58         int rval, width;
59
60         (void) setlocale(LC_CTYPE, "");
61
62         width = -1;
63         previous_ch = 0;
64         while ((ch = getopt(argc, argv, "0123456789bsw:")) != -1) {
65                 switch (ch) {
66                 case 'b':
67                         bflag = 1;
68                         break;
69                 case 's':
70                         sflag = 1;
71                         break;
72                 case 'w':
73                         if ((width = atoi(optarg)) <= 0) {
74                                 errx(1, "illegal width value");
75                         }
76                         break;
77                 case '0': case '1': case '2': case '3': case '4':
78                 case '5': case '6': case '7': case '8': case '9':
79                         /* Accept a width as eg. -30. Note that a width
80                          * specified using the -w option is always used prior
81                          * to this undocumented option. */
82                         switch (previous_ch) {
83                         case '0': case '1': case '2': case '3': case '4':
84                         case '5': case '6': case '7': case '8': case '9':
85                                 /* The width is a number with multiple digits:
86                                  * add the last one. */
87                                 width = width * 10 + (ch - '0');
88                                 break;
89                         default:
90                                 /* Set the width, unless it was previously
91                                  * set. For instance, the following options
92                                  * would all give a width of 5 and not 10:
93                                  *   -10 -w5
94                                  *   -5b10
95                                  *   -5 -10b */
96                                 if (width == -1)
97                                         width = ch - '0';
98                                 break;
99                         }
100                         break;
101                 default:
102                         usage();
103                 }
104                 previous_ch = ch;
105         }
106         argv += optind;
107         argc -= optind;
108
109         if (width == -1)
110                 width = DEFLINEWIDTH;
111         rval = 0;
112         if (!*argv)
113                 fold(width);
114         else for (; *argv; ++argv)
115                 if (!freopen(*argv, "r", stdin)) {
116                         warn("%s", *argv);
117                         rval = 1;
118                 } else
119                         fold(width);
120         exit(rval);
121 }
122
123 static void
124 usage(void)
125 {
126         (void)fprintf(stderr, "usage: fold [-bs] [-w width] [file ...]\n");
127         exit(1);
128 }
129
130 /*
131  * Fold the contents of standard input to fit within WIDTH columns (or bytes)
132  * and write to standard output.
133  *
134  * If sflag is set, split the line at the last space character on the line.
135  * This flag necessitates storing the line in a buffer until the current
136  * column > width, or a newline or EOF is read.
137  *
138  * The buffer can grow larger than WIDTH due to backspaces and carriage
139  * returns embedded in the input stream.
140  */
141 void
142 fold(int width)
143 {
144         static wchar_t *buf;
145         static int buf_max;
146         int col, i, indx, space;
147         wint_t ch;
148
149         col = indx = 0;
150         while ((ch = getwchar()) != WEOF) {
151                 if (ch == '\n') {
152                         wprintf(L"%.*ls\n", indx, buf);
153                         col = indx = 0;
154                         continue;
155                 }
156                 if ((col = newpos(col, ch)) > width) {
157                         if (sflag) {
158                                 i = indx;
159                                 while (--i >= 0 && !iswblank(buf[i]))
160                                         ;
161                                 space = i;
162                         }
163                         if (sflag && space != -1) {
164                                 space++;
165                                 wprintf(L"%.*ls\n", space, buf);
166                                 wmemmove(buf, buf + space, indx - space);
167                                 indx -= space;
168                                 col = 0;
169                                 for (i = 0; i < indx; i++)
170                                         col = newpos(col, buf[i]);
171                         } else {
172                                 wprintf(L"%.*ls\n", indx, buf);
173                                 col = indx = 0;
174                         }
175                         col = newpos(col, ch);
176                 }
177                 if (indx + 1 > buf_max) {
178                         buf_max += LINE_MAX;
179                         buf = realloc(buf, sizeof(*buf) * buf_max);
180                         if (buf == NULL)
181                                 err(1, "realloc()");
182                 }
183                 buf[indx++] = ch;
184         }
185
186         if (indx != 0)
187                 wprintf(L"%.*ls", indx, buf);
188 }
189
190 /*
191  * Update the current column position for a character.
192  */
193 static int
194 newpos(int col, wint_t ch)
195 {
196         char buf[MB_LEN_MAX];
197         size_t len;
198         int w;
199
200         if (bflag) {
201                 len = wcrtomb(buf, ch, NULL);
202                 col += len;
203         } else
204                 switch (ch) {
205                 case '\b':
206                         if (col > 0)
207                                 --col;
208                         break;
209                 case '\r':
210                         col = 0;
211                         break;
212                 case '\t':
213                         col = (col + 8) & ~7;
214                         break;
215                 default:
216                         if ((w = wcwidth(ch)) > 0)
217                                 col += w;
218                         break;
219                 }
220
221         return (col);
222 }