This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / sys / isofs / isofs_rrip.c
CommitLineData
b4e4339f
JH
1/*
2 * Copyright (c) 1993 Atsushi Murai (amurai@spec.co.jp)
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
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * 4. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY Atsushi Murai(amurai@spec.co.jp)``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
fde1aeb2 31 * $Id: isofs_rrip.c,v 1.4 1993/11/25 01:32:27 wollman Exp $
b4e4339f
JH
32 */
33
34#include "param.h"
dd18dc33 35#include "systm.h"
b4e4339f
JH
36#include "namei.h"
37#include "buf.h"
38#include "file.h"
39#include "vnode.h"
40#include "mount.h"
41#include "kernel.h"
42
43#include "sys/time.h"
44
45#include "iso.h"
46#include "isofs_node.h"
47#include "isofs_rrip.h"
48#include "iso_rrip.h"
49
50/*
51 * POSIX file attribute
52 */
53static int isofs_rrip_attr( p, ana )
4c45483e
GW
54 ISO_RRIP_ATTR *p;
55 ISO_RRIP_ANALYZE *ana;
b4e4339f
JH
56{
57 ana->inode.iso_mode = isonum_731(p->mode_l);
58 ana->inode.iso_uid = (uid_t)isonum_731(p->uid_l);
59 ana->inode.iso_gid = (gid_t)isonum_731(p->gid_l);
60/* ana->inode.iso_links = isonum_731(p->links_l); */
4c45483e 61 return 0;
b4e4339f
JH
62}
63
64int isofs_rrip_defattr( isodir, ana )
4c45483e
GW
65 struct iso_directory_record *isodir;
66 ISO_RRIP_ANALYZE *ana;
b4e4339f
JH
67{
68 ana->inode.iso_mode = (VREAD|VEXEC|(VREAD|VEXEC)>>3|(VREAD|VEXEC)>>6);
69 ana->inode.iso_uid = (uid_t)0;
70 ana->inode.iso_gid = (gid_t)0;
4c45483e 71 return 0;
b4e4339f
JH
72}
73
74/*
75 * POSIX device modes
76 */
77static int isofs_rrip_device( p, ana )
4c45483e
GW
78 ISO_RRIP_DEVICE *p;
79 ISO_RRIP_ANALYZE *ana;
b4e4339f 80{
b4e4339f
JH
81 char buf[3];
82
83 buf[0] = p->h.type[0];
84 buf[1] = p->h.type[1];
85 buf[2] = 0x00;
86
87 printf("isofs:%s[%d] high=0x%08x, low=0x%08x\n",
88 buf,
89 isonum_711(p->h.length),
90 isonum_731(p->dev_t_high_l),
91 isonum_731(p->dev_t_low_l)
92 );
4c45483e 93 return 0;
b4e4339f
JH
94}
95
96/*
97 * Symbolic Links
98 */
99static int isofs_rrip_slink( p, ana )
4c45483e
GW
100 ISO_RRIP_SLINK *p;
101 ISO_RRIP_ANALYZE *ana;
b4e4339f 102{
4c45483e 103 return 0;
b4e4339f
JH
104}
105
106/*
107 * Alternate name
108 */
109static int isofs_rrip_altname( p, ana )
4c45483e
GW
110 ISO_RRIP_ALTNAME *p;
111 ISO_RRIP_ANALYZE *ana;
b4e4339f 112{
4c45483e 113 return 0;
b4e4339f
JH
114}
115
116/*
117 * Child Link
118 */
119static int isofs_rrip_clink( p, ana )
4c45483e
GW
120 ISO_RRIP_CLINK *p;
121 ISO_RRIP_ANALYZE *ana;
b4e4339f 122{
b4e4339f
JH
123 char buf[3];
124 buf[0] = p->h.type[0];
125 buf[1] = p->h.type[1];
126 buf[2] = 0x00;
127 printf("isofs:%s[%d] loc=%d\n",
128 buf,
129 isonum_711(p->h.length),
130 isonum_733(p->dir_loc)
131 );
b4e4339f 132 ana->inode.iso_cln = isonum_733(p->dir_loc);
4c45483e 133 return 0;
b4e4339f
JH
134}
135
136/*
137 * Parent Link
138 */
139static int isofs_rrip_plink( p, ana )
140ISO_RRIP_PLINK *p;
141ISO_RRIP_ANALYZE *ana;
142{
143
b4e4339f
JH
144 char buf[3];
145 buf[0] = p->h.type[0];
146 buf[1] = p->h.type[1];
147 buf[2] = 0x00;
148 printf("isofs:%s[%d] loc=%d\n",
149 buf,
150 isonum_711(p->h.length),
151 isonum_733(p->dir_loc)
152 );
b4e4339f 153 ana->inode.iso_pln = isonum_733(p->dir_loc);
4c45483e 154 return 0;
b4e4339f
JH
155}
156
157/*
158 * Relocated directory
159 */
160static int isofs_rrip_reldir( p, ana )
161ISO_RRIP_RELDIR *p;
162ISO_RRIP_ANALYZE *ana;
163{
b4e4339f
JH
164 char buf[3];
165
166 buf[0] = p->h.type[0];
167 buf[1] = p->h.type[1];
168 buf[2] = 0x00;
169
170 printf("isofs:%s[%d]\n",buf, isonum_711(p->h.length) );
4c45483e 171 return 0;
b4e4339f
JH
172}
173
174/*
175 * Time stamp
176 */
177static void isofs_rrip_tstamp_conv7(pi, pu)
178char *pi;
179struct timeval *pu;
180{
181 int i;
182 int crtime,days;
183 int year,month,day,hour,minute,second,tz;
184
185 year = pi[0] - 70;
186 month = pi[1];
187 day = pi[2];
188 hour = pi[3];
189 minute = pi[4];
190 second = pi[5];
191 tz = pi[6];
192
193 if (year < 0) {
194 crtime = 0;
195 } else {
196 int monlen[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
197 days = year * 365;
198 if (year > 2)
199 days += (year+2) / 4;
200 for (i = 1; i < month; i++)
201 days += monlen[i-1];
202 if (((year+2) % 4) == 0 && month > 2)
203 days++;
204 days += day - 1;
205 crtime = ((((days * 24) + hour) * 60 + minute) * 60)
206 + second;
207
208 /* sign extend */
209 if (tz & 0x80)
210 tz |= (-1 << 8);
211
212 /* timezone offset is unreliable on some disks */
213 if (-48 <= tz && tz <= 52)
214 crtime += tz * 15 * 60;
215 }
216 pu->tv_sec = crtime;
217 pu->tv_usec = 0;
218}
219
220
221static unsigned isofs_chars2ui( begin, end )
222unsigned char *begin;
223unsigned char *end;
224{
225 unsigned rc=0;
226 int len;
227 int wlen;
228 static int pow[]={ 1, 10, 100, 1000 };
229
230 len = end - begin;
231 wlen= len;
232 for (; len >= 0; len -- ) {
233 rc += ( *(begin+len) * pow[wlen - len] );
234 }
235 return( rc );
236}
237
238static void isofs_rrip_tstamp_conv17(pi, pu)
239unsigned char *pi;
240struct timeval *pu;
241{
242 unsigned char buf[7];
243
244 /* year:"0001"-"9999" -> -1900 */
245 buf[0] = (unsigned char)(isofs_chars2ui( &pi[0], &pi[3]) - 1900 );
246
247 /* month: " 1"-"12" -> 1 - 12 */
248 buf[1] = (unsigned char)isofs_chars2ui( &pi[4], &pi[5]);
249
250 /* day: " 1"-"31" -> 1 - 31 */
251 buf[2] = isofs_chars2ui( &pi[6], &pi[7]);
252
253 /* hour: " 0"-"23" -> 0 - 23 */
254 buf[3] = isofs_chars2ui( &pi[8], &pi[9]);
255
256 /* minute:" 0"-"59" -> 0 - 59 */
257 buf[4] = isofs_chars2ui( &pi[10], &pi[11] );
258
259 /* second:" 0"-"59" -> 0 - 59 */
260 buf[5] = isofs_chars2ui( &pi[12], &pi[13] );
261
262 /* difference of GMT */
263 buf[6] = pi[16];
264
265 isofs_rrip_tstamp_conv7(buf, pu);
266}
267
268static int isofs_rrip_tstamp( p, ana )
269ISO_RRIP_TSTAMP *p;
270ISO_RRIP_ANALYZE *ana;
271{
272 unsigned char *ptime;
273
274 ptime = p->time;
275
276 /* Check a format of time stamp (7bytes/17bytes) */
277 if ( !(*p->flags & ISO_SUSP_TSTAMP_FORM17 ) ) {
278 isofs_rrip_tstamp_conv7(ptime, &ana->inode.iso_ctime );
279
280 if ( *p->flags & ISO_SUSP_TSTAMP_MODIFY )
281 isofs_rrip_tstamp_conv7(ptime+7, &ana->inode.iso_mtime );
282 else
283 ana->inode.iso_mtime = ana->inode.iso_ctime;
284
285 if ( *p->flags & ISO_SUSP_TSTAMP_ACCESS )
286 isofs_rrip_tstamp_conv7(ptime+14, &ana->inode.iso_atime );
287 else
288 ana->inode.iso_atime = ana->inode.iso_ctime;
289 } else {
290 isofs_rrip_tstamp_conv17(ptime, &ana->inode.iso_ctime );
291
292 if ( *p->flags & ISO_SUSP_TSTAMP_MODIFY )
293 isofs_rrip_tstamp_conv17(ptime+17, &ana->inode.iso_mtime );
294 else
295 ana->inode.iso_mtime = ana->inode.iso_ctime;
296
297 if ( *p->flags & ISO_SUSP_TSTAMP_ACCESS )
298 isofs_rrip_tstamp_conv17(ptime+34, &ana->inode.iso_atime );
299 else
300 ana->inode.iso_atime = ana->inode.iso_ctime;
301 }
4c45483e 302 return 0;
b4e4339f
JH
303}
304
305int isofs_rrip_deftstamp( isodir, ana )
4c45483e
GW
306 struct iso_directory_record *isodir;
307 ISO_RRIP_ANALYZE *ana;
b4e4339f
JH
308{
309 isofs_rrip_tstamp_conv7(isodir->date, &ana->inode.iso_ctime );
310 ana->inode.iso_atime = ana->inode.iso_ctime;
311 ana->inode.iso_mtime = ana->inode.iso_ctime;
4c45483e 312 return 0;
b4e4339f
JH
313}
314
315
316/*
317 * Flag indicating
318 * Nothing to do....
319 */
320static int isofs_rrip_idflag( p, ana )
321ISO_RRIP_IDFLAG *p;
322ISO_RRIP_ANALYZE *ana;
323{
b4e4339f
JH
324 char buf[3];
325
326 buf[0] = p->h.type[0];
327 buf[1] = p->h.type[1];
328 buf[2] = 0x00;
329
330 printf("isofs:%s[%d] idflag=0x%x\n",
331 buf,
332 isonum_711(p->h.length),
333 p->flags );
4c45483e 334 return 0;
b4e4339f
JH
335}
336
337/*
338 * Extension reference
339 * Nothing to do....
340 */
341static int isofs_rrip_exflag( p, ana )
342ISO_RRIP_EXFLAG *p;
343ISO_RRIP_ANALYZE *ana;
344{
b4e4339f
JH
345 char buf[3];
346
347 buf[0] = p->h.type[0];
348 buf[1] = p->h.type[1];
349 buf[2] = 0x00;
350
351 printf("isofs:%s[%d] exflag=0x%x",
352 buf,
353 isonum_711(p->h.length),
354 p->flags );
4c45483e 355 return 0;
b4e4339f
JH
356}
357
358/*
359 * Unknown ...
360 * Nothing to do....
361 */
362static int isofs_rrip_unknown( p, ana )
4c45483e
GW
363 ISO_RRIP_EXFLAG *p;
364 ISO_RRIP_ANALYZE *ana;
b4e4339f 365{
4c45483e 366 return 0;
b4e4339f
JH
367}
368
369typedef struct {
370 char type[2];
371 int (*func)();
372 int (*func2)();
373 int result;
374} RRIP_TABLE;
375
376static RRIP_TABLE rrip_table [] = {
377 { 'P', 'X', isofs_rrip_attr, isofs_rrip_defattr, ISO_SUSP_ATTR },
378 { 'T', 'F', isofs_rrip_tstamp, isofs_rrip_deftstamp, ISO_SUSP_TSTAMP },
379 { 'N', 'M', isofs_rrip_altname,0, ISO_SUSP_ALTNAME },
380 { 'C', 'L', isofs_rrip_clink, 0, ISO_SUSP_CLINK },
381 { 'P', 'L', isofs_rrip_plink, 0, ISO_SUSP_PLINK },
382 { 'S', 'L', isofs_rrip_slink, 0, ISO_SUSP_SLINK },
383 { 'R', 'E', isofs_rrip_reldir, 0, ISO_SUSP_RELDIR },
384 { 'P', 'N', isofs_rrip_device, 0, ISO_SUSP_DEVICE },
385 { 'R', 'R', isofs_rrip_idflag, 0, ISO_SUSP_IDFLAG },
386 { 'E', 'R', isofs_rrip_exflag, 0, ISO_SUSP_EXFLAG },
387 { 'S', 'P', isofs_rrip_unknown,0, ISO_SUSP_UNKNOWN },
388 { 'C', 'E', isofs_rrip_unknown,0, ISO_SUSP_UNKNOWN }
389};
390
391int isofs_rrip_analyze ( isodir, analyze )
392struct iso_directory_record *isodir;
393ISO_RRIP_ANALYZE *analyze;
394{
395 register RRIP_TABLE *ptable;
396 register ISO_SUSP_HEADER *phead;
397 register ISO_SUSP_HEADER *pend;
398 int found;
399 int i;
400 char *pwhead;
401 int result;
402
403 /*
404 * Note: If name length is odd,
405 * it will be padding 1 byte after the name
406 */
407 pwhead = isodir->name + isonum_711(isodir->name_len);
408 if ( !(isonum_711(isodir->name_len) & 0x0001) )
409 pwhead ++;
410 phead = (ISO_SUSP_HEADER *)pwhead;
411 pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) );
412
413 result = 0;
414 if ( pend == phead ) {
415 goto setdefault;
416 }
417 /*
418 * Note: "pend" should be more than one SUSP header
419 */
420 while ( pend >= phead + 1) {
421 found = 0;
422 for ( ptable=&rrip_table[0];ptable < &rrip_table[sizeof(rrip_table)/sizeof(RRIP_TABLE)]; ptable ++) {
423 if ( bcmp( phead->type, ptable->type, 2 ) == 0 ) {
424 found = 1;
425 ptable->func( phead, analyze );
426 result |= ptable->result;
427 break;
428 }
429 }
430 if ( found == 0 ) {
b4e4339f
JH
431 printf("isofs: name '");
432 for ( i =0; i < isonum_711(isodir->name_len) ;i++) {
433 printf("%c", *(isodir->name + i) );
434 }
435 printf("'");
436 printf(" - type %c%c [%08x/%08x]...not found\n",
437 phead->type[0], phead->type[1], phead, pend );
438 isofs_hexdump( phead, (int)( (char *)pend - (char *)phead ) );
b4e4339f
JH
439 break;
440 }
441
442 /*
443 * move to next SUSP
444 */
445 phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead);
446 }
447
448setdefault:
449 /*
450 * If we don't find the Basic SUSP stuffs, just set default value
451 * ( attribute/time stamp )
452 */
453 for ( ptable=&rrip_table[0];ptable < &rrip_table[2]; ptable ++) {
454 if ( ptable->func2 != 0 && !(ptable->result & result) ) {
455 ptable->func2( isodir, analyze );
456 }
457 }
458 return ( result );
459}
460
461/*
462 * Get Alternate Name from 'AL' record
d115dd9c 463 * If either no AL record nor 0 lenght,
b4e4339f
JH
464 * it will be return the translated ISO9660 name,
465 */
466int isofs_rrip_getname( isodir, outbuf, outlen )
fde1aeb2
GW
467 struct iso_directory_record *isodir;
468 char *outbuf;
469 int *outlen;
b4e4339f
JH
470{
471 ISO_SUSP_HEADER *phead, *pend;
472 ISO_RRIP_ALTNAME *p;
473 char *pwhead;
474 int found;
475
476 /*
477 * Note: If name length is odd,
478 * it will be padding 1 byte after the name
479 */
480 pwhead = isodir->name + isonum_711(isodir->name_len);
481 if ( !(isonum_711(isodir->name_len) & 0x0001) )
482 pwhead ++;
483 phead = (ISO_SUSP_HEADER *)pwhead;
484 pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) );
485
486 found = 0;
487 if ( pend != phead ) {
488 while ( pend >= phead + 1) {
489 if ( bcmp( phead->type, "NM", 2 ) == 0 ) {
490 found = 1;
491 break;
492 }
493 phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead);
494 }
495 }
496 if ( found == 1 ) {
497 p = (ISO_RRIP_ALTNAME *)phead;
498 *outlen = isonum_711( p->h.length ) - sizeof( ISO_RRIP_ALTNAME );
499 bcopy( (char *)( &p->flags + 1 ), outbuf, *outlen );
500 } else {
501 isofntrans(isodir->name, isonum_711(isodir->name_len), outbuf, outlen );
502 if ( *outlen == 1) {
503 switch ( outbuf[0] ) {
504 case 0:
505 outbuf[0] = '.';
506 break;
507 case 1:
508 outbuf[0] = '.';
509 outbuf[1] = '.';
510 *outlen = 2;
511 }
512 }
513 }
514 return( found );
515}
516
517/*
518 * Get Symbolic Name from 'SL' record
519 *
520 * Note: isodir should contains SL record!
521 */
522int isofs_rrip_getsymname( vp, isodir, outbuf, outlen )
523struct vnode *vp;
524struct iso_directory_record *isodir;
525char *outbuf;
526int *outlen;
527{
528 register ISO_RRIP_SLINK_COMPONENT *pcomp;
529 register ISO_SUSP_HEADER *phead, *pend;
530 register ISO_RRIP_SLINK_COMPONENT *pcompe;
531 ISO_RRIP_SLINK *p;
532 char *pwhead;
533 int found;
534 int slash;
535 int wlen;
536
537 /*
538 * Note: If name length is odd,
539 * it will be padding 1 byte after the name
540 */
541 pwhead = isodir->name + isonum_711(isodir->name_len);
542 if ( !(isonum_711(isodir->name_len) & 0x0001) )
543 pwhead ++;
544 phead = (ISO_SUSP_HEADER *)pwhead;
545 pend = (ISO_SUSP_HEADER *)( (char *)isodir + isonum_711(isodir->length) );
546
547 found = 0;
548 if ( pend != phead ) {
549 while ( pend >= phead + 1) {
550 if ( bcmp( phead->type, "SL", 2 ) == 0 ) {
551 found = 1;
552 break;
553 }
554 phead = (ISO_SUSP_HEADER *) ((unsigned)isonum_711(phead->length) + (char *)phead);
555 }
556 }
557 if ( found == 0 ) {
558 *outlen = 0;
559 return( found );
560 }
561
562 p = (ISO_RRIP_SLINK *)phead;
563 pcomp = (ISO_RRIP_SLINK_COMPONENT *)p->component;
564 pcompe = (ISO_RRIP_SLINK_COMPONENT *)((char *)p + isonum_711(p->h.length));
565
566 /*
567 * Gathering a Symbolic name from each component with path
568 */
569 *outlen = 0;
570 slash = 0;
571 while ( pcomp < pcompe ) {
572
573 /* Inserting Current */
574 if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_CURRENT ) {
575 bcopy("./", outbuf+*outlen, 2);
576 *outlen += 2;
577 slash = 0;
578 }
579
580 /* Inserting Parent */
581 if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_PARENT ) {
582 bcopy("../", outbuf+*outlen, 3);
583 *outlen += 3;
584 slash = 0;
585 }
586
587 /* Inserting slash for ROOT */
588 if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_ROOT ) {
589 bcopy("/", outbuf+*outlen, 1);
590 *outlen += 1;
591 slash = 0;
592 }
593
594 /* Inserting a mount point i.e. "/cdrom" */
595 if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_VOLROOT ) {
596 wlen = strlen(vp->v_mount->mnt_stat.f_mntonname);
597 bcopy(vp->v_mount->mnt_stat.f_mntonname,outbuf+*outlen, wlen);
598 *outlen += wlen;
599 slash = 1;
600 }
601
602 /* Inserting hostname i.e. "tama:" */
603 if ( pcomp->cflag[0] & ISO_SUSP_CFLAG_HOST ) {
604 bcopy(hostname, outbuf+*outlen, hostnamelen);
605 *(outbuf+hostnamelen) = ':';
606 *outlen += (hostnamelen + 1);
607 slash = 0; /* Uuum Should we insert a slash ? */
608 }
609
610 /* Inserting slash for next component */
611 if ( slash == 1 ) {
612 outbuf[*outlen] = '/';
613 *outlen += 1;
614 slash = 0;
615 }
616
617 /* Inserting component */
618 wlen = isonum_711(pcomp->clen);
619 if ( wlen != 0 ) {
620 bcopy( pcomp->name, outbuf + *outlen, wlen );
621 *outlen += wlen;
622 slash = 1;
623 }
624
625 /*
626 * Move to next component...
627 */
628 pcomp = (ISO_RRIP_SLINK_COMPONENT *)((char *)pcomp
629 + sizeof(ISO_RRIP_SLINK_COMPONENT) - 1
630 + isonum_711(pcomp->clen));
631 }
632 return( found );
633}
b4e4339f
JH
634/* Hexdump routine for debug*/
635int isofs_hexdump( p, size )
636 unsigned char *p;
637 int size;
638{
639 int i,j,k;
640 unsigned char *wp;
641
642 for ( i = 0; i < size; i += 16 ) {
643 printf("isofs: ");
644 wp = p;
645 k = ( (size - i) > 16 ? 16 : size - i );
646 for ( j =0; j < k; j ++ ){
647 printf("%02x ", *p );
648 p++;
649 }
650 printf(" : ");
651 p = wp;
652 for ( j =0; j < k; j ++ ){
653 if ( (*p > 0x20) && (*p < 0x7f) )
654 printf("%c", *p );
655 else
656 printf(" ");
657 p++;
658 }
659 printf("\n");
660 }
661 printf("\n");
4c45483e 662 return 0;
b4e4339f 663}