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 #define itor(index) R_INDEX_TO_ARGV[index]
44 
45 #define	IS_SWAP_TARGET(index)				\
46 	NULL != R_ARGV_ARG1[itor(index)] &&		\
47 		( ('@' == *R_ARGV_ARG1[itor(index)] && '\n' == *retu_p) || \
48 		  ('@' == *R_ARGV_ARG1[itor(index)] && '\0' == *retu_p) || \
49 		  (0 == strncmp(R_ARGV_ARG1[itor(index)], retu_p, \
50 		                strlen(R_ARGV_ARG1[itor(index)]))))
51 
52 #define SWAPED_STRING(index)				\
53 	R_ARGV_ARG2[itor(index)]
54 
55 static const char atmark[2] = "@";
56 
57 static inline void do_swap_print(FILE *fp);
58 
59 static bool outputed;
60 
61 void
swap_print(void)62 swap_print(void)
63 {
64 	FILE *fp;
65 
66 	outputed = false;
67 
68 	// Process the first file
69 	int file_i = 1;
70 	char line_buf[LINE_BUF_MAX];
71 
72 	fp = fopen(F_ARGV[file_i], "r");
73 	if (NULL == fp)
74 		err(errno, "%s", F_ARGV[file_i]);
75 
76 	// Delete the first line
77 	if (FLAG_1)
78 		fgets(line_buf, LINE_BUF_MAX, fp);
79 
80 	do_swap_print(fp);
81 
82 	fclose(fp);
83 
84 	// Process second and subsequent files
85 	for (file_i = 2; file_i <= F_ARGC; file_i++) {
86 		fp = fopen(F_ARGV[file_i], "r");
87 		if (NULL == fp)
88 			err(errno, "%s", F_ARGV[file_i]);
89 
90 		do_swap_print(fp);
91 
92 		fclose(fp);
93 	}
94 
95 	// If option -e is specified, only one line is output if
96 	// there is no output.
97 	if (FLAG_e && ! outputed) {
98 		int outputed_retu_count = 0;
99 		for (int i = 1; i <=R_INDEX_MAX; i++) {
100 			if (R_INDEX_EXIST[i]) {
101 				if (NULL != R_ARGV_ARG2[R_INDEX_TO_ARGV[i]])
102 					printf("%s",
103 					   R_ARGV_ARG2[R_INDEX_TO_ARGV[i]]);
104 				else
105 					printf("@");
106 				++outputed_retu_count;
107 				if (R_ARGC == outputed_retu_count)
108 					putchar('\n');
109 				else
110 					putchar(' ');
111 			}
112 		}
113 	}
114 }
115 
116 static inline void
do_swap_print(FILE * fp)117 do_swap_print(FILE *fp)
118 {
119 	char line_buf[LINE_BUF_MAX];
120 	char *p;
121 	char *retu_p;
122 
123 	// Array to access the string of columns from the number of
124 	// columns:
125 	// 	retu_to_string[RETU_NUMBER] --> RETU_STRING
126 	//
127 	char *retu_to_string[1 + R_INDEX_MAX];
128 
129 	// 0 is always access to the atmark.
130 	retu_to_string[0] = atmark;
131 
132 	while (NULL != fgets(line_buf, LINE_BUF_MAX, fp)) {
133 		// Divide the line_buf by column and set access method
134 		// (retu_to_string[RETU_NUMBER] --> RETU_STRING).
135 		p = line_buf;
136 
137 		for (int retu_i = 1; retu_i <= R_INDEX_MAX;
138 			retu_i++) {
139 
140 			// Pointer to the column string
141 			retu_p = p;
142 
143 			// Move to end of string
144 			while (IS_NOT_SEPARATOR(p))
145 				++p;
146 
147 			// Set access if selected column
148 			if (R_INDEX_EXIST[retu_i]) {
149 				if (IS_SWAP_TARGET(retu_i)) {
150 					retu_to_string[retu_i] =
151 						SWAPED_STRING(retu_i);
152 				}
153 				else {
154 					if (retu_p == p)
155 						retu_to_string[retu_i] =
156 							atmark;
157 					else
158 						retu_to_string[retu_i] =
159 							retu_p;
160 				}
161 			}
162 
163 			// Move to beginning of next column
164 			if (IS_SPACE(p)) {
165 				*p = '\0';
166 				++p;
167 			}
168 			// Since there is no line data left, everything
169 			// is set to access "@" or swaped string.
170 			else {
171 				if (IS_LF(p))
172 					*p = '\0';
173 
174 				for (++retu_i; retu_i <= R_INDEX_MAX;
175 					retu_i++) {
176 					if (R_INDEX_EXIST[retu_i]) {
177 						if (NULL != R_ARGV_ARG2[R_INDEX_TO_ARGV[retu_i]])
178 							retu_to_string[retu_i] =
179 								R_ARGV_ARG2[R_INDEX_TO_ARGV[retu_i]];
180 						else
181 							retu_to_string[retu_i] =
182 								atmark;
183 					}
184 				}
185 			}
186 		}
187 
188 		// Output in specified column order
189 		for (int retu_i = 1; retu_i < R_ARGC; retu_i++)
190 			printf("%s ", retu_to_string[R_ARGV[retu_i]]);
191 		printf("%s\n", retu_to_string[R_ARGV[R_ARGC]]);
192 
193 		outputed = true;
194 	}
195 }
196