1 /* $OpenBSD: lam.c,v 1.24 2021/12/03 15:15:22 deraadt Exp $ */ 2 /* $NetBSD: lam.c,v 1.2 1994/11/14 20:27:42 jtc Exp $ */ 3 4 /*- 5 * Copyright (c) 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * lam - laminate files 35 * Author: John Kunze, UCB 36 */ 37 38 #include <sys/types.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <locale.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <limits.h> 46 #include <string.h> 47 #include <unistd.h> 48 49 #define BIGBUFSIZ 5 * BUFSIZ 50 51 struct openfile { /* open file structure */ 52 FILE *fp; /* file pointer */ 53 int minwidth; /* pad this column to this width */ 54 int maxwidth; /* truncate this column */ 55 short eof; /* eof flag */ 56 short pad; /* pad flag for missing columns */ 57 char eol; /* end of line character */ 58 char align; /* '0' for zero fill, '-' for left align */ 59 char *sepstring; /* string to print before each line */ 60 } *input; 61 int inputsize; /* number of openfile entries */ 62 63 int output; /* line output produced */ 64 int nofinalnl; /* normally append \n to each output line */ 65 char line[BIGBUFSIZ]; 66 char *linep; 67 68 int mbswidth_truncate(char *, int); /* utf8.c */ 69 70 void usage(void); 71 char *gatherline(struct openfile *); 72 void getargs(int, char *[]); 73 char *pad(struct openfile *); 74 75 int 76 main(int argc, char *argv[]) 77 { 78 int i; 79 80 setlocale(LC_CTYPE, ""); 81 82 if (pledge("stdio rpath", NULL) == -1) 83 err(1, "pledge"); 84 85 getargs(argc, argv); 86 if (inputsize == 0) 87 usage(); 88 89 if (pledge("stdio", NULL) == -1) 90 err(1, "pledge"); 91 92 /* Concatenate lines from each file, then print. */ 93 for (;;) { 94 linep = line; 95 /* 96 * For each file that has a line to print, output is 97 * incremented. Thus if numfiles is 0, we are done. 98 */ 99 output = 0; 100 for (i = 0; i < inputsize && input[i].fp != NULL; i++) 101 linep = gatherline(&input[i]); 102 if (output == 0) 103 exit(0); 104 fputs(line, stdout); 105 /* Print terminating -s argument. */ 106 fputs(input[i].sepstring, stdout); 107 if (!nofinalnl) 108 putchar('\n'); 109 } 110 } 111 112 void 113 getargs(int argc, char *argv[]) 114 { 115 struct openfile *ip; 116 const char *errstr; 117 char *p, *q; 118 void *tmp; 119 int ch, P, S, F, T; 120 121 input = calloc(inputsize+1, sizeof *input); 122 if (input == NULL) 123 errx(1, "too many files"); 124 ip = &input[inputsize]; 125 126 P = S = F = T = 0; /* capitalized options */ 127 while (optind < argc) { 128 switch (ch = getopt(argc, argv, "F:f:P:p:S:s:T:t:")) { 129 case 'P': case 'p': 130 P = (ch == 'P'); 131 ip->pad = 1; 132 /* FALLTHROUGH */ 133 case 'F': case 'f': 134 F = (ch == 'F'); 135 /* Validate format string argument. */ 136 p = optarg; 137 if (*p == '0' || *p == '-') 138 ip->align = *p++; 139 else 140 ip->align = ' '; 141 if ((q = strchr(p, '.')) != NULL) 142 *q++ = '\0'; 143 if (*p != '\0') { 144 ip->minwidth = strtonum(p, 1, INT_MAX, 145 &errstr); 146 if (errstr != NULL) 147 errx(1, "minimum width is %s: %s", 148 errstr, p); 149 } 150 if (q != NULL) { 151 ip->maxwidth = strtonum(q, 1, INT_MAX, 152 &errstr); 153 if (errstr != NULL) 154 errx(1, "maximum width is %s: %s", 155 errstr, q); 156 } else 157 ip->maxwidth = INT_MAX; 158 break; 159 case 'S': case 's': 160 S = (ch == 'S'); 161 ip->sepstring = optarg; 162 break; 163 case 'T': case 't': 164 T = (ch == 'T'); 165 if (strlen(optarg) != 1) 166 usage(); 167 ip->eol = optarg[0]; 168 nofinalnl = 1; 169 break; 170 case -1: 171 if (optind >= argc) 172 break; /* to support "--" */ 173 /* This is a file, not a flag. */ 174 if (strcmp(argv[optind], "-") == 0) 175 ip->fp = stdin; 176 else if ((ip->fp = fopen(argv[optind], "r")) == NULL) 177 err(1, "%s", argv[optind]); 178 ip->pad = P; 179 if (ip->sepstring == NULL) 180 ip->sepstring = S ? (ip-1)->sepstring : ""; 181 if (ip->eol == '\0') 182 ip->eol = T ? (ip-1)->eol : '\n'; 183 if (ip->align == '\0') { 184 if (F || P) { 185 ip->align = (ip-1)->align; 186 ip->minwidth = (ip-1)->minwidth; 187 ip->maxwidth = (ip-1)->maxwidth; 188 } else 189 ip->maxwidth = INT_MAX; 190 } 191 192 ++inputsize; 193 194 /* Prepare for next file argument */ 195 tmp = recallocarray(input, inputsize, 196 inputsize+1, sizeof *input); 197 if (tmp == NULL) 198 errx(1, "too many files"); 199 input = tmp; 200 ip = &input[inputsize]; 201 optind++; 202 break; 203 default: 204 usage(); 205 /* NOTREACHED */ 206 } 207 } 208 ip->fp = NULL; 209 if (ip->sepstring == NULL) 210 ip->sepstring = ""; 211 } 212 213 char * 214 pad(struct openfile *ip) 215 { 216 size_t n; 217 char *lp = linep; 218 int i = 0; 219 220 n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp); 221 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp); 222 if (ip->pad) 223 while (i++ < ip->minwidth && lp + 1 < line + sizeof(line)) 224 *lp++ = ' '; 225 *lp = '\0'; 226 return (lp); 227 } 228 229 /* 230 * Grab line from file, appending to linep. Increments printed if file 231 * is still open. 232 */ 233 char * 234 gatherline(struct openfile *ip) 235 { 236 size_t n; 237 char s[BUFSIZ]; 238 char *p; 239 char *lp = linep; 240 char *end = s + BUFSIZ - 1; 241 int c, width; 242 243 if (ip->eof) 244 return (pad(ip)); 245 for (p = s; (c = fgetc(ip->fp)) != EOF && p < end; p++) 246 if ((*p = c) == ip->eol) 247 break; 248 *p = '\0'; 249 if (c == EOF) { 250 ip->eof = 1; 251 if (ip->fp == stdin) 252 fclose(stdin); 253 return (pad(ip)); 254 } 255 /* Something will be printed. */ 256 output++; 257 n = strlcpy(lp, ip->sepstring, line + sizeof(line) - lp); 258 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp); 259 width = mbswidth_truncate(s, ip->maxwidth); 260 if (ip->align != '-') 261 while (width++ < ip->minwidth && lp + 1 < line + sizeof(line)) 262 *lp++ = ip->align; 263 n = strlcpy(lp, s, line + sizeof(line) - lp); 264 lp += (n < line + sizeof(line) - lp) ? n : strlen(lp); 265 if (ip->align == '-') 266 while (width++ < ip->minwidth && lp + 1 < line + sizeof(line)) 267 *lp++ = ' '; 268 *lp = '\0'; 269 return (lp); 270 } 271 272 void 273 usage(void) 274 { 275 extern char *__progname; 276 277 fprintf(stderr, 278 "usage: %s [-F|f min.max] [-P|p min.max] [-S|s sepstring] [-T|t c] file ...\n", 279 __progname); 280 exit(1); 281 } 282