Print this page
| Split |
Close |
| Expand all |
| Collapse all |
--- old/usr/src/cmd/ptools/pflags/pflags.c
+++ new/usr/src/cmd/ptools/pflags/pflags.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
|
↓ open down ↓ |
17 lines elided |
↑ open up ↑ |
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 /*
28 - * Copyright (c) 2013, Joyent, Inc. All rights reserved.
28 + * Copyright 2015, Joyent, Inc.
29 29 */
30 30
31 31 #include <stdio.h>
32 32 #include <stdio_ext.h>
33 33 #include <stdlib.h>
34 34 #include <unistd.h>
35 35 #include <ctype.h>
36 36 #include <fcntl.h>
37 37 #include <strings.h>
38 38 #include <dirent.h>
39 39 #include <errno.h>
40 40 #include <sys/types.h>
41 41 #include <sys/int_fmtio.h>
42 42 #include <libproc.h>
43 43
44 44 typedef struct look_arg {
45 45 int pflags;
46 46 const char *lwps;
47 47 int count;
48 48 } look_arg_t;
49 49
50 50 static int look(char *);
51 51 static int lwplook(look_arg_t *, const lwpstatus_t *, const lwpsinfo_t *);
52 52 static char *prflags(int);
53 53 static char *prwhy(int);
54 54 static char *prwhat(int, int);
55 55 static void dumpregs(const prgregset_t, int);
56 56
57 57 static char *command;
58 58 static struct ps_prochandle *Pr;
59 59
60 60 static int is64; /* Is current process 64-bit? */
61 61 static int rflag; /* Show registers? */
62 62
63 63 #define LWPFLAGS \
64 64 (PR_STOPPED|PR_ISTOP|PR_DSTOP|PR_ASLEEP|PR_PCINVAL|PR_STEP \
65 65 |PR_AGENT|PR_DETACH|PR_DAEMON)
66 66
67 67 #define PROCFLAGS \
68 68 (PR_ISSYS|PR_VFORKP|PR_ORPHAN|PR_NOSIGCHLD|PR_WAITPID \
69 69 |PR_FORK|PR_RLC|PR_KLC|PR_ASYNC|PR_BPTADJ|PR_MSACCT|PR_MSFORK|PR_PTRACE)
70 70
71 71 #define ALLFLAGS (LWPFLAGS|PROCFLAGS)
72 72
73 73 int
74 74 main(int argc, char **argv)
75 75 {
76 76 int rc = 0;
77 77 int errflg = 0;
78 78 int opt;
79 79 struct rlimit rlim;
80 80
81 81 if ((command = strrchr(argv[0], '/')) != NULL)
82 82 command++;
83 83 else
84 84 command = argv[0];
85 85
86 86 /* options */
87 87 while ((opt = getopt(argc, argv, "r")) != EOF) {
88 88 switch (opt) {
89 89 case 'r': /* show registers */
90 90 rflag = 1;
91 91 break;
92 92 default:
93 93 errflg = 1;
94 94 break;
95 95 }
96 96 }
97 97
98 98 argc -= optind;
99 99 argv += optind;
100 100
101 101 if (errflg || argc <= 0) {
102 102 (void) fprintf(stderr,
103 103 "usage:\t%s [-r] { pid | core }[/lwps] ...\n", command);
104 104 (void) fprintf(stderr, " (report process status flags)\n");
105 105 (void) fprintf(stderr, " -r : report registers\n");
106 106 return (2);
107 107 }
108 108
109 109 /*
110 110 * Make sure we'll have enough file descriptors to handle a target
111 111 * that has many many mappings.
112 112 */
113 113 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
114 114 rlim.rlim_cur = rlim.rlim_max;
115 115 (void) setrlimit(RLIMIT_NOFILE, &rlim);
116 116 (void) enable_extended_FILE_stdio(-1, -1);
117 117 }
118 118
119 119 while (argc-- > 0)
120 120 rc += look(*argv++);
121 121
122 122 return (rc);
123 123 }
124 124
125 125 static int
126 126 look(char *arg)
127 127 {
128 128 int gcode;
129 129 int gcode2;
130 130 pstatus_t pstatus;
131 131 psinfo_t psinfo;
132 132 int flags;
133 133 sigset_t sigmask;
134 134 fltset_t fltmask;
135 135 sysset_t entryset;
136 136 sysset_t exitset;
137 137 uint32_t sigtrace, sigtrace1, sigtrace2, fltbits;
138 138 uint32_t sigpend, sigpend1, sigpend2;
139 139 uint32_t *bits;
140 140 char buf[PRSIGBUFSZ];
141 141 look_arg_t lookarg;
142 142
143 143 if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY,
144 144 PGRAB_RETAIN | PGRAB_FORCE | PGRAB_RDONLY | PGRAB_NOSTOP, &gcode,
145 145 &lookarg.lwps)) == NULL) {
146 146 if (gcode == G_NOPROC &&
147 147 proc_arg_psinfo(arg, PR_ARG_PIDS, &psinfo, &gcode2) > 0 &&
148 148 psinfo.pr_nlwp == 0) {
149 149 (void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
150 150 return (0);
151 151 }
152 152 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
153 153 command, arg, Pgrab_error(gcode));
154 154 return (1);
155 155 }
156 156
157 157 (void) memcpy(&pstatus, Pstatus(Pr), sizeof (pstatus_t));
158 158 (void) memcpy(&psinfo, Ppsinfo(Pr), sizeof (psinfo_t));
159 159 proc_unctrl_psinfo(&psinfo);
160 160
161 161 if (psinfo.pr_nlwp == 0) {
162 162 (void) printf("%d:\t<defunct>\n\n", (int)psinfo.pr_pid);
163 163 Prelease(Pr, PRELEASE_RETAIN);
164 164 return (0);
165 165 }
166 166
167 167 is64 = (pstatus.pr_dmodel == PR_MODEL_LP64);
168 168
169 169 sigmask = pstatus.pr_sigtrace;
170 170 fltmask = pstatus.pr_flttrace;
171 171 entryset = pstatus.pr_sysentry;
172 172 exitset = pstatus.pr_sysexit;
173 173
174 174 if (Pstate(Pr) == PS_DEAD) {
175 175 (void) printf("core '%s' of %d:\t%.70s\n",
176 176 arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
177 177 } else {
178 178 (void) printf("%d:\t%.70s\n",
179 179 (int)psinfo.pr_pid, psinfo.pr_psargs);
180 180 }
181 181
182 182 (void) printf("\tdata model = %s", is64? "_LP64" : "_ILP32");
183 183 if ((flags = (pstatus.pr_flags & PROCFLAGS)) != 0)
184 184 (void) printf(" flags = %s", prflags(flags));
185 185 (void) printf("\n");
186 186
187 187 fltbits = *((uint32_t *)&fltmask);
188 188 if (fltbits)
189 189 (void) printf("\tflttrace = 0x%.8x\n", fltbits);
190 190
191 191 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */
192 192 sigtrace = *((uint32_t *)&sigmask);
193 193 sigtrace1 = *((uint32_t *)&sigmask + 1);
194 194 sigtrace2 = *((uint32_t *)&sigmask + 2);
195 195 #else
196 196 #error "fix me: MAXSIG out of bounds"
197 197 #endif
198 198 if (sigtrace | sigtrace1 | sigtrace2)
199 199 (void) printf("\tsigtrace = 0x%.8x 0x%.8x 0x%.8x\n\t %s\n",
200 200 sigtrace, sigtrace1, sigtrace2,
201 201 proc_sigset2str(&sigmask, "|", 1, buf, sizeof (buf)));
202 202
203 203 bits = ((uint32_t *)&entryset);
204 204 if (bits[0] | bits[1] | bits[2] | bits[3] |
205 205 bits[4] | bits[5] | bits[6] | bits[7])
206 206 (void) printf(
207 207 "\tentryset = "
208 208 "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
209 209 "\t "
210 210 "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
211 211 bits[0], bits[1], bits[2], bits[3],
212 212 bits[4], bits[5], bits[6], bits[7]);
213 213
214 214 bits = ((uint32_t *)&exitset);
215 215 if (bits[0] | bits[1] | bits[2] | bits[3] |
216 216 bits[4] | bits[5] | bits[6] | bits[7])
217 217 (void) printf(
218 218 "\texitset = "
219 219 "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n"
220 220 "\t "
221 221 "0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
222 222 bits[0], bits[1], bits[2], bits[3],
223 223 bits[4], bits[5], bits[6], bits[7]);
224 224
225 225 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */
226 226 sigpend = *((uint32_t *)&pstatus.pr_sigpend);
227 227 sigpend1 = *((uint32_t *)&pstatus.pr_sigpend + 1);
228 228 sigpend2 = *((uint32_t *)&pstatus.pr_sigpend + 2);
229 229 #else
230 230 #error "fix me: MAXSIG out of bounds"
231 231 #endif
232 232 if (sigpend | sigpend1 | sigpend2)
233 233 (void) printf("\tsigpend = 0x%.8x,0x%.8x,0x%.8x\n",
234 234 sigpend, sigpend1, sigpend2);
235 235
236 236 lookarg.pflags = pstatus.pr_flags;
237 237 lookarg.count = 0;
238 238 (void) Plwp_iter_all(Pr, (proc_lwp_all_f *)lwplook, &lookarg);
239 239
240 240 if (lookarg.count == 0)
241 241 (void) printf("No matching lwps found");
242 242
243 243 (void) printf("\n");
244 244 Prelease(Pr, PRELEASE_RETAIN);
245 245
246 246 return (0);
247 247 }
248 248
249 249 static int
250 250 lwplook_zombie(const lwpsinfo_t *pip)
251 251 {
252 252 (void) printf(" /%d:\t<defunct>\n", (int)pip->pr_lwpid);
253 253 return (0);
254 254 }
255 255
256 256 static int
257 257 lwplook(look_arg_t *arg, const lwpstatus_t *psp, const lwpsinfo_t *pip)
258 258 {
259 259 int flags;
260 260 uint32_t sighold, sighold1, sighold2;
261 261 uint32_t sigpend, sigpend1, sigpend2;
262 262 psinfo_t ps;
263 263 int cursig;
264 264 char buf[32];
265 265
266 266 if (!proc_lwp_in_set(arg->lwps, pip->pr_lwpid))
267 267 return (0);
268 268
269 269 arg->count++;
270 270
271 271 if (psp == NULL)
272 272 return (lwplook_zombie(pip));
273 273
274 274 /*
275 275 * PR_PCINVAL is just noise if the lwp is not stopped.
276 276 * Don't bother reporting it unless the lwp is stopped.
277 277 */
278 278 flags = psp->pr_flags & LWPFLAGS;
279 279 if (!(flags & PR_STOPPED))
280 280 flags &= ~PR_PCINVAL;
281 281
282 282 (void) printf(" /%d:\tflags = %s", (int)psp->pr_lwpid, prflags(flags));
283 283 if ((flags & PR_ASLEEP) || (psp->pr_syscall &&
284 284 !(arg->pflags & PR_ISSYS))) {
285 285 if (flags & PR_ASLEEP) {
286 286 if ((flags & ~PR_ASLEEP) != 0)
287 287 (void) printf("|");
288 288 (void) printf("ASLEEP");
289 289 }
290 290 if (psp->pr_syscall && !(arg->pflags & PR_ISSYS)) {
291 291 uint_t i;
292 292
293 293 (void) printf(" %s(",
294 294 proc_sysname(psp->pr_syscall, buf, sizeof (buf)));
295 295 for (i = 0; i < psp->pr_nsysarg; i++) {
296 296 if (i != 0)
297 297 (void) printf(",");
298 298 (void) printf("0x%lx", psp->pr_sysarg[i]);
299 299 }
300 300 (void) printf(")");
301 301 }
302 302 }
303 303 (void) printf("\n");
304 304
305 305 if (flags & PR_STOPPED) {
306 306 (void) printf("\twhy = %s", prwhy(psp->pr_why));
307 307 if (psp->pr_why != PR_REQUESTED &&
308 308 psp->pr_why != PR_SUSPENDED)
309 309 (void) printf(" what = %s",
310 310 prwhat(psp->pr_why, psp->pr_what));
311 311 (void) printf("\n");
312 312 }
313 313
314 314 #if (MAXSIG > 2 * 32) && (MAXSIG <= 3 * 32) /* assumption */
315 315 sighold = *((uint32_t *)&psp->pr_lwphold);
316 316 sighold1 = *((uint32_t *)&psp->pr_lwphold + 1);
317 317 sighold2 = *((uint32_t *)&psp->pr_lwphold + 2);
318 318 sigpend = *((uint32_t *)&psp->pr_lwppend);
319 319 sigpend1 = *((uint32_t *)&psp->pr_lwppend + 1);
320 320 sigpend2 = *((uint32_t *)&psp->pr_lwppend + 2);
321 321 #else
322 322 #error "fix me: MAXSIG out of bounds"
323 323 #endif
324 324 cursig = psp->pr_cursig;
325 325
326 326 if (sighold | sighold1 | sighold2)
327 327 (void) printf("\tsigmask = 0x%.8x,0x%.8x,0x%.8x\n",
328 328 sighold, sighold1, sighold2);
329 329 if (sigpend | sigpend1 | sigpend2)
330 330 (void) printf("\tlwppend = 0x%.8x,0x%.8x,0x%.8x\n",
331 331 sigpend, sigpend1, sigpend2);
332 332 if (cursig)
333 333 (void) printf("\tcursig = %s\n",
334 334 proc_signame(cursig, buf, sizeof (buf)));
335 335
336 336 if ((flags & PR_AGENT) &&
337 337 Plwp_getspymaster(Pr, pip->pr_lwpid, &ps) == 0) {
338 338 time_t time = ps.pr_time.tv_sec;
339 339 char t[64];
340 340
341 341 (void) strftime(t, sizeof (t), "%F:%H.%M.%S", localtime(&time));
342 342
343 343 (void) printf("\tspymaster = pid %d, \"%s\" at %s\n",
344 344 (int)ps.pr_pid, ps.pr_psargs, t);
345 345 }
346 346
347 347 if (rflag) {
348 348 if (Pstate(Pr) == PS_DEAD || (arg->pflags & PR_STOPPED)) {
349 349 dumpregs(psp->pr_reg, is64);
350 350 } else {
351 351 (void) printf("\tNot stopped, can't show registers\n");
352 352 }
353 353 }
354 354
355 355 return (0);
356 356 }
357 357
358 358 static char *
359 359 prflags(int arg)
360 360 {
361 361 static char code_buf[200];
362 362 char *str = code_buf;
363 363
364 364 if (arg == 0)
365 365 return ("0");
366 366
367 367 if (arg & ~ALLFLAGS)
368 368 (void) sprintf(str, "0x%x", arg & ~ALLFLAGS);
369 369 else
370 370 *str = '\0';
371 371
372 372 /*
373 373 * Display the semi-permanent lwp flags first.
374 374 */
375 375 if (arg & PR_DAEMON) /* daemons are always detached so */
376 376 (void) strcat(str, "|DAEMON");
377 377 else if (arg & PR_DETACH) /* report detach only if non-daemon */
378 378 (void) strcat(str, "|DETACH");
379 379
380 380 if (arg & PR_STOPPED)
381 381 (void) strcat(str, "|STOPPED");
382 382 if (arg & PR_ISTOP)
383 383 (void) strcat(str, "|ISTOP");
384 384 if (arg & PR_DSTOP)
385 385 (void) strcat(str, "|DSTOP");
386 386 #if 0 /* displayed elsewhere */
387 387 if (arg & PR_ASLEEP)
388 388 (void) strcat(str, "|ASLEEP");
389 389 #endif
390 390 if (arg & PR_PCINVAL)
391 391 (void) strcat(str, "|PCINVAL");
392 392 if (arg & PR_STEP)
393 393 (void) strcat(str, "|STEP");
394 394 if (arg & PR_AGENT)
395 395 (void) strcat(str, "|AGENT");
396 396 if (arg & PR_ISSYS)
397 397 (void) strcat(str, "|ISSYS");
398 398 if (arg & PR_VFORKP)
399 399 (void) strcat(str, "|VFORKP");
400 400 if (arg & PR_ORPHAN)
401 401 (void) strcat(str, "|ORPHAN");
402 402 if (arg & PR_NOSIGCHLD)
403 403 (void) strcat(str, "|NOSIGCHLD");
404 404 if (arg & PR_WAITPID)
405 405 (void) strcat(str, "|WAITPID");
406 406 if (arg & PR_FORK)
407 407 (void) strcat(str, "|FORK");
408 408 if (arg & PR_RLC)
409 409 (void) strcat(str, "|RLC");
410 410 if (arg & PR_KLC)
411 411 (void) strcat(str, "|KLC");
412 412 if (arg & PR_ASYNC)
413 413 (void) strcat(str, "|ASYNC");
414 414 if (arg & PR_BPTADJ)
415 415 (void) strcat(str, "|BPTADJ");
416 416 if (arg & PR_MSACCT)
417 417 (void) strcat(str, "|MSACCT");
418 418 if (arg & PR_MSFORK)
419 419 (void) strcat(str, "|MSFORK");
420 420 if (arg & PR_PTRACE)
421 421 (void) strcat(str, "|PTRACE");
422 422
423 423 if (*str == '|')
424 424 str++;
425 425
426 426 return (str);
427 427 }
428 428
429 429 static char *
430 430 prwhy(int why)
431 431 {
432 432 static char buf[20];
433 433 char *str;
434 434
435 435 switch (why) {
436 436 case PR_REQUESTED:
437 437 str = "PR_REQUESTED";
438 438 break;
439 439 case PR_SIGNALLED:
440 440 str = "PR_SIGNALLED";
441 441 break;
442 442 case PR_SYSENTRY:
443 443 str = "PR_SYSENTRY";
444 444 break;
445 445 case PR_SYSEXIT:
446 446 str = "PR_SYSEXIT";
|
↓ open down ↓ |
408 lines elided |
↑ open up ↑ |
447 447 break;
448 448 case PR_JOBCONTROL:
449 449 str = "PR_JOBCONTROL";
450 450 break;
451 451 case PR_FAULTED:
452 452 str = "PR_FAULTED";
453 453 break;
454 454 case PR_SUSPENDED:
455 455 str = "PR_SUSPENDED";
456 456 break;
457 + case PR_BRAND:
458 + str = "PR_BRAND";
459 + break;
457 460 default:
458 461 str = buf;
459 462 (void) sprintf(str, "%d", why);
460 463 break;
461 464 }
462 465
463 466 return (str);
464 467 }
465 468
466 469 static char *
467 470 prwhat(int why, int what)
468 471 {
469 472 static char buf[32];
470 473 char *str;
471 474
472 475 switch (why) {
473 476 case PR_SIGNALLED:
474 477 case PR_JOBCONTROL:
475 478 str = proc_signame(what, buf, sizeof (buf));
476 479 break;
477 480 case PR_SYSENTRY:
478 481 case PR_SYSEXIT:
479 482 str = proc_sysname(what, buf, sizeof (buf));
480 483 break;
481 484 case PR_FAULTED:
482 485 str = proc_fltname(what, buf, sizeof (buf));
483 486 break;
484 487 default:
485 488 (void) sprintf(str = buf, "%d", what);
486 489 break;
487 490 }
488 491
489 492 return (str);
490 493 }
491 494
492 495 #if defined(__amd64)
493 496 static const char * const regname[NPRGREG] = {
494 497 "%r15", "%r14", "%r13", "%r12", "%r11", "%r10", " %r9", " %r8",
495 498 "%rdi", "%rsi", "%rbp", "%rbx", "%rdx", "%rcx", "%rax", "%trapno",
496 499 "%err", "%rip", " %cs", "%rfl", "%rsp", " %ss", " %fs", " %gs",
497 500 " %es", " %ds", "%fsbase", "%gsbase"
498 501 };
499 502
500 503 static const char * const regname32[NPRGREG32] = {
501 504 " %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
502 505 "%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
503 506 "%efl", "%uesp", " %ss"
504 507 };
505 508
506 509 /* XX64 Do we want to expose this through libproc */
507 510 void
508 511 prgregset_n_to_32(const prgreg_t *src, prgreg32_t *dst)
509 512 {
510 513 bzero(dst, NPRGREG32 * sizeof (prgreg32_t));
511 514 dst[GS] = src[REG_GS];
512 515 dst[FS] = src[REG_FS];
513 516 dst[DS] = src[REG_DS];
514 517 dst[ES] = src[REG_ES];
515 518 dst[EDI] = src[REG_RDI];
516 519 dst[ESI] = src[REG_RSI];
517 520 dst[EBP] = src[REG_RBP];
518 521 dst[EBX] = src[REG_RBX];
519 522 dst[EDX] = src[REG_RDX];
520 523 dst[ECX] = src[REG_RCX];
521 524 dst[EAX] = src[REG_RAX];
522 525 dst[TRAPNO] = src[REG_TRAPNO];
523 526 dst[ERR] = src[REG_ERR];
524 527 dst[EIP] = src[REG_RIP];
525 528 dst[CS] = src[REG_CS];
526 529 dst[EFL] = src[REG_RFL];
527 530 dst[UESP] = src[REG_RSP];
528 531 dst[SS] = src[REG_SS];
529 532 }
530 533
531 534 #elif defined(__i386)
532 535 static const char * const regname[NPRGREG] = {
533 536 " %gs", " %fs", " %es", " %ds", "%edi", "%esi", "%ebp", "%esp",
534 537 "%ebx", "%edx", "%ecx", "%eax", "%trapno", "%err", "%eip", " %cs",
535 538 "%efl", "%uesp", " %ss"
536 539 };
537 540 #endif /* __i386 */
538 541
539 542 #if defined(__amd64) && defined(_LP64)
540 543 static void
541 544 dumpregs32(const prgregset_t reg)
542 545 {
543 546 prgregset32_t reg32;
544 547 int i;
545 548
546 549 prgregset_n_to_32(reg, reg32);
547 550
548 551 for (i = 0; i < NPRGREG32; i++) {
549 552 (void) printf(" %s = 0x%.8X",
550 553 regname32[i], reg32[i]);
551 554 if ((i+1) % 4 == 0)
552 555 (void) putchar('\n');
553 556 }
554 557 if (i % 4 != 0)
555 558 (void) putchar('\n');
556 559 }
557 560 #endif
558 561
559 562 static void
560 563 dumpregs(const prgregset_t reg, int is64)
561 564 {
562 565 int width = is64? 16 : 8;
563 566 int cols = is64? 2 : 4;
564 567 int i;
565 568
566 569 #if defined(__amd64) && defined(_LP64)
567 570 if (!is64) {
568 571 dumpregs32(reg);
569 572 return;
570 573 }
571 574 #endif
572 575
573 576 for (i = 0; i < NPRGREG; i++) {
574 577 (void) printf(" %s = 0x%.*lX",
575 578 regname[i], width, (long)reg[i]);
576 579 if ((i+1) % cols == 0)
577 580 (void) putchar('\n');
578 581 }
579 582 if (i % cols != 0)
580 583 (void) putchar('\n');
581 584 }
|
↓ open down ↓ |
115 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX