8
8
#include <stdlib.h>
9
9
#include <string.h>
10
10
#include <fcntl.h>
11
- #include <sys/select.h>
12
- #include <sys/wait.h>
13
11
#include <termios.h>
12
+ #include <poll.h>
13
+ #include <errno.h>
14
14
15
15
#define MSG_MAX 512 /* guaranteed max message length */
16
16
#define CHA_MAX 200 /* gauranteed max channel length */
@@ -42,20 +42,6 @@ log_append(char *str, char *path) {
42
42
fclose (out );
43
43
}
44
44
45
- static int
46
- kbhit (void ) {
47
-
48
- int byteswaiting ;
49
- struct timespec ts = {0 };
50
- fd_set fds ;
51
-
52
- FD_ZERO (& fds );
53
- FD_SET (0 , & fds );
54
- byteswaiting = pselect (1 , & fds , NULL , NULL , & ts , NULL );
55
-
56
- return byteswaiting > 0 ;
57
- }
58
-
59
45
static void
60
46
raw (char * fmt , ...) {
61
47
@@ -154,73 +140,84 @@ raw_parser(char *usrin) {
154
140
} else printw ("%*s\x1b[33;1m%-.*s\x1b[0m %s" , s , "" , g , nickname , message );
155
141
}
156
142
157
- static void
158
- parent_process (int fd [], pid_t pid ) {
159
- char usrin [MSG_MAX ], v1 [MSG_MAX - CHA_MAX ], v2 [CHA_MAX ], c1 ;
160
- struct termios tp , save ;
143
+ static char message_buffer [MSG_MAX + 1 ];
144
+ /* The first character after the end */
145
+ static size_t message_end = 0 ;
161
146
162
- tcgetattr (STDIN_FILENO , & tp );
163
- save = tp ;
164
- tp .c_cc [VERASE ] = 127 ;
165
-
166
- if (tcsetattr (STDIN_FILENO , TCSANOW , & tp ) < 0 ) exit (2 );
167
-
168
- while (waitpid (pid , NULL , WNOHANG ) == 0 ) {
169
- if (!kbhit ()) dprintf (fd [1 ], "/\n" );
170
- else if (fgets (usrin , MSG_MAX , stdin ) != NULL &&
171
- (sscanf (usrin , "/%[m] %s %[^\n]\n" , & c1 , v2 , v1 ) > 2 ||
172
- sscanf (usrin , "/%[xMQqnjp] %[^\n]\n" , & c1 , v1 ) > 0 )) {
173
- switch (c1 ) {
174
- case 'x' : dprintf (fd [1 ], "%s\n" , v1 ); break ;
175
- case 'q' : dprintf (fd [1 ], "quit\n" ); break ;
176
- case 'Q' : dprintf (fd [1 ], "quit %s\n" , v1 ); break ;
177
- case 'j' : dprintf (fd [1 ], "join %s\n" , v1 ); break ;
178
- case 'p' : dprintf (fd [1 ], "part %s\n" , v1 ); break ;
179
- case 'n' : dprintf (fd [1 ], "names #%s\n" , chan ); break ;
180
- case 'M' : dprintf (fd [1 ], "privmsg nickserv :%s\n" , v1 ); break ;
181
- case 'm' : dprintf (fd [1 ], "privmsg %s :%s\n" , v2 , v1 ); break ;
147
+ static int
148
+ handle_server_message (void ) {
149
+ for (;;) {
150
+ ssize_t sl = read (conn , & message_buffer [message_end ], MSG_MAX - message_end );
151
+ if (sl == -1 ) {
152
+ if (errno == EAGAIN || errno == EWOULDBLOCK ) {
153
+ /* Let's wait for new input */
154
+ return 0 ;
155
+ } else {
156
+ perror ("read" );
157
+ return -2 ;
182
158
}
183
- } else dprintf (fd [1 ], "privmsg #%s :%s" , chan , usrin );
184
- }
185
-
186
- if (tcsetattr (STDIN_FILENO , TCSANOW , & save ) < 0 ) exit (2 );
187
-
188
- perror ("Connection closed" );
189
- }
190
-
191
- static void
192
- child_process (int fd []) {
193
- int sl , i , o = 0 ;
194
- char u [MSG_MAX ], s , b [MSG_MAX ];
195
-
196
- irc_init ();
159
+ }
160
+ if (sl == 0 ) {
161
+ fputs ("Connection closed\n" , stderr );
162
+ return -1 ;
163
+ }
197
164
198
- raw ( "NICK %s\r\n" , nick ) ;
199
- raw ( "USER %s - - :%s\r\n" , ( user ? user : nick ), ( real ? real : nick )) ;
165
+ size_t old_message_end = message_end ;
166
+ message_end += sl ;
200
167
201
- if (pass ) raw ("PASS %s\r\n" , pass );
202
- if (inic ) raw ("%s\r\n" , inic );
168
+ /* Iterate over the added part to find \r\n */
169
+ for (size_t i = old_message_end ; i < message_end ; ++ i ) {
170
+ if (i != 0 && message_buffer [i - 1 ] == '\r' && message_buffer [i ] == '\n' ) {
171
+ /* Cut the buffer here for parsing */
172
+ char saved_char = message_buffer [i + 1 ];
173
+ message_buffer [i + 1 ] = '\0' ;
174
+ raw_parser (message_buffer );
175
+ message_buffer [i + 1 ] = saved_char ;
203
176
204
- while (( sl = read ( conn , & s , 1 ))) {
205
- if ( sl > 0 ) b [ o ] = s ;
177
+ /* Move the part after the parsed line to the beginning */
178
+ memmove ( & message_buffer , & message_buffer [ i + 1 ], message_end - i - 1 ) ;
206
179
207
- if ((o > 0 && b [o - 1 ] == '\r' && b [o ] == '\n' ) || o == MSG_MAX ) {
208
- b [o + 1 ] = '\0' ;
209
- raw_parser (b );
210
- o = 0 ;
211
- } else if (sl > 0 ) ++ o ;
180
+ /* There might still be other lines to be read */
181
+ message_end = message_end - i - 1 ;
182
+ i = 0 ;
183
+ }
184
+ }
185
+ if (message_end == MSG_MAX ) {
186
+ /* The buffer is full and doesn't contain \r\n */
187
+ message_end = 0 ;
188
+ }
189
+ }
190
+ }
212
191
213
- if (read (fd [0 ], u , MSG_MAX ) > 0 ) {
214
- for (i = 0 ; u [i ] != '\n' ; ++ i ) continue ;
215
- if (u [0 ] != '/' ) raw ("%-*.*s\r\n" , i , i , u );
192
+ static void
193
+ handle_user_input (void ) {
194
+ char usrin [MSG_MAX ], v1 [MSG_MAX - CHA_MAX ], v2 [CHA_MAX ], c1 ;
195
+ if (fgets (usrin , MSG_MAX , stdin ) != NULL &&
196
+ (sscanf (usrin , "/%[m] %s %[^\n]\n" , & c1 , v2 , v1 ) > 2 ||
197
+ sscanf (usrin , "/%[xMQqnjp] %[^\n]\n" , & c1 , v1 ) > 0 )) {
198
+ switch (c1 ) {
199
+ case 'x' : raw ("%s\r\n" , v1 ); break ;
200
+ case 'q' : raw ("quit\r\n" ); break ;
201
+ case 'Q' : raw ("quit %s\r\n" , v1 ); break ;
202
+ case 'j' : raw ("join %s\r\n" , v1 ); break ;
203
+ case 'p' : raw ("part %s\r\n" , v1 ); break ;
204
+ case 'n' : raw ("names #%s\r\n" , chan ); break ;
205
+ case 'M' : raw ("privmsg nickserv :%s\r\n" , v1 ); break ;
206
+ case 'm' : raw ("privmsg %s :%s\r\n" , v2 , v1 ); break ;
216
207
}
208
+ } else {
209
+ size_t msg_len = strlen (usrin );
210
+ /* Remove the trailing newline to add a carriage return */
211
+ if (usrin [msg_len - 1 ] == '\n' )
212
+ usrin [msg_len - 1 ] = '\0' ;
213
+ raw ("privmsg #%s :%s\r\n" , chan , usrin );
217
214
}
218
215
}
219
216
220
217
int
221
218
main (int argc , char * * argv ) {
222
219
223
- int fd [ 2 ], cval ;
220
+ int cval ;
224
221
225
222
while ((cval = getopt (argc , argv , "s:p:o:n:k:c:u:r:x:w:W:hvV" )) != -1 ) {
226
223
switch (cval ) {
@@ -247,16 +244,56 @@ main(int argc, char **argv) {
247
244
return 1 ;
248
245
}
249
246
250
- if (pipe (fd ) < 0 ) {
251
- perror ("Pipe() failed" );
252
- return 1 ;
253
- }
247
+ irc_init ();
248
+
249
+ raw ("NICK %s\r\n" , nick );
250
+ raw ("USER %s - - :%s\r\n" , (user ? user : nick ), (real ? real : nick ));
251
+
252
+ if (pass ) raw ("PASS %s\r\n" , pass );
253
+ if (inic ) raw ("%s\r\n" , inic );
254
+
255
+ struct termios tp , save ;
256
+
257
+ tcgetattr (STDIN_FILENO , & tp );
258
+ save = tp ;
259
+ tp .c_cc [VERASE ] = 127 ;
260
+
261
+ if (tcsetattr (STDIN_FILENO , TCSANOW , & tp ) < 0 ) return 2 ;
262
+
263
+ struct pollfd fds [2 ];
264
+ fds [0 ].fd = STDIN_FILENO ;
265
+ fds [1 ].fd = conn ;
266
+ fds [0 ].events = POLLIN ;
267
+ fds [1 ].events = POLLIN ;
268
+
269
+ int return_code = 0 ;
254
270
255
- pid_t pid = fork ();
271
+ for (;;) {
272
+ int poll_res = poll (fds , 2 , -1 );
256
273
257
- switch (pid ) {
258
- case -1 : perror ("Fork() failed" ); return 1 ;
259
- case 0 : child_process (fd ); return 0 ;
260
- default : parent_process (fd , pid ); return 0 ;
274
+ if (poll_res != -1 ) {
275
+ if (fds [0 ].revents & POLLIN ) {
276
+ handle_user_input ();
277
+ }
278
+
279
+ if (fds [1 ].revents & POLLIN ) {
280
+ int rc = handle_server_message ();
281
+ /* Has the server closed the connection? */
282
+ if (rc != 0 ) {
283
+ if (rc == -2 ) return_code = EXIT_FAILURE ;
284
+ goto end ;
285
+ };
286
+ }
287
+ } else {
288
+ if (errno == EAGAIN ) continue ;
289
+ perror ("poll" );
290
+ return_code = EXIT_FAILURE ;
291
+ goto end ;
292
+ }
261
293
}
294
+
295
+ end :
296
+ if (tcsetattr (STDIN_FILENO , TCSANOW , & save ) < 0 ) return 2 ;
297
+
298
+ return return_code ;
262
299
}
0 commit comments