1 /*
2  * Copyright (c) 2019 Daichi GOTO
3  * 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 are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "command.h"
29 
30 #define LINE_BUF_MAX	3145728
31 
32 #define	IS_SEPARATOR(p)					\
33 	' ' == *p || '\n' == *p || '\0' == *p
34 #define	IS_NOT_SEPARATOR(p)				\
35 	' ' != *p && '\n' != *p && '\0' != *p
36 
37 #define	IS_SPACE(p)					\
38 	' ' == *p
39 
40 #define	IS_LF(p)					\
41 	'\n' == *p
42 
43 static const char atmark[2] = "@";
44 
45 static inline void do_fast_print(FILE *fp);
46 
47 static bool outputed;
48 
49 void
fast_print(void)50 fast_print(void)
51 {
52 	FILE *fp;
53 
54 	outputed = false;
55 
56 	// Process the first file
57 	int file_i = 1;
58 	char line_buf[LINE_BUF_MAX];
59 
60 	fp = fopen(F_ARGV[file_i], "r");
61 	if (NULL == fp)
62 		err(errno, "%s", F_ARGV[file_i]);
63 
64 	// Delete the first line
65 	if (FLAG_1)
66 		fgets(line_buf, LINE_BUF_MAX, fp);
67 
68 	do_fast_print(fp);
69 
70 	fclose(fp);
71 
72 	// Process second and subsequent files
73 	for (file_i = 2; file_i <= F_ARGC; file_i++) {
74 		fp = fopen(F_ARGV[file_i], "r");
75 		if (NULL == fp)
76 			err(errno, "%s", F_ARGV[file_i]);
77 
78 		do_fast_print(fp);
79 
80 		fclose(fp);
81 	}
82 
83 	// If option -e is specified, only one line is output if
84 	// there is no output.
85 	if (FLAG_e && ! outputed) {
86 		for (int i = 1; i < R_ARGC; i++)
87 			printf("@ ");
88 		printf("@\n");
89 	}
90 }
91 
92 static inline void
do_fast_print(FILE * fp)93 do_fast_print(FILE *fp)
94 {
95 	char line_buf[LINE_BUF_MAX];
96 	char *p;
97 	char *retu_p;
98 
99 	// Array to access the string of columns from the number of
100 	// columns:
101 	// 	retu_to_string[RETU_NUMBER] --> RETU_STRING
102 	//
103 	char *retu_to_string[1 + R_INDEX_MAX];
104 
105 	// 0 is always access to the atmark.
106 	retu_to_string[0] = atmark;
107 
108 	while (NULL != fgets(line_buf, LINE_BUF_MAX, fp)) {
109 		// Divide the line_buf by column and set access method
110 		// (retu_to_string[RETU_NUMBER] --> RETU_STRING).
111 		p = line_buf;
112 
113 		for (int retu_i = 1; retu_i <= R_INDEX_MAX;
114 			retu_i++) {
115 
116 			// Pointer to the column string
117 			retu_p = p;
118 
119 			// Move to end of string
120 			while (IS_NOT_SEPARATOR(p))
121 				++p;
122 
123 			// Set access if selected column
124 			if (R_INDEX_EXIST[retu_i]) {
125 				if (retu_p == p)
126 					retu_to_string[retu_i] = atmark;
127 				else
128 					retu_to_string[retu_i] = retu_p;
129 			}
130 
131 			// Move to beginning of next column
132 			if (IS_SPACE(p)) {
133 				*p = '\0';
134 				++p;
135 			}
136 			// Since there is no line data left, everything
137 			// is set to access "@"
138 			else {
139 				if (IS_LF(p))
140 					*p = '\0';
141 
142 				for (++retu_i; retu_i <= R_INDEX_MAX;
143 					retu_i++) {
144 					if (R_INDEX_EXIST[retu_i])
145 						retu_to_string[retu_i] =
146 							atmark;
147 				}
148 			}
149 		}
150 
151 		// Output in specified column order
152 		for (int retu_i = 1; retu_i < R_ARGC; retu_i++)
153 			printf("%s ", retu_to_string[R_ARGV[retu_i]]);
154 		printf("%s\n", retu_to_string[R_ARGV[R_ARGC]]);
155 
156 		outputed = true;
157 	}
158 }
159