8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libpkg / common / dstream.c
blob6622b74361a822a7e06271954db58d54ded9033b
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2017 Peter Tribble.
27 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
28 * Use is subject to license terms.
31 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
32 /* All Rights Reserved */
36 #include <stdio.h>
37 #include <string.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/sysmacros.h>
44 #include <errno.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/statvfs.h>
48 #include <fcntl.h>
49 #include "pkglib.h"
50 #include "pkglibmsgs.h"
51 #include "pkglocale.h"
53 /* libadm.a */
54 extern char *devattr(char *device, char *attribute);
55 extern int pkgnmchk(register char *pkg, register char *spec,
56 int presvr4flg);
57 extern int getvol(char *device, char *label, int options, char *prompt);
59 #define CMDSIZ 512
60 #define LSIZE 128
61 #define DDPROC "/usr/bin/dd"
62 #define CPIOPROC "/usr/bin/cpio"
64 /* device types */
66 #define G_TM_TAPE 1 /* Tapemaster controller */
67 #define G_XY_DISK 3 /* xy disks */
68 #define G_SD_DISK 7 /* scsi sd disk */
69 #define G_XT_TAPE 8 /* xt tapes */
70 #define G_SF_FLOPPY 9 /* sf floppy */
71 #define G_XD_DISK 10 /* xd disks */
72 #define G_ST_TAPE 11 /* scsi tape */
73 #define G_NS 12 /* noswap pseudo-dev */
74 #define G_RAM 13 /* ram pseudo-dev */
75 #define G_FT 14 /* tftp */
76 #define G_HD 15 /* 386 network disk */
77 #define G_FD 16 /* 386 AT disk */
78 #define G_FILE 28 /* file, not a device */
79 #define G_NO_DEV 29 /* device does not require special treatment */
80 #define G_DEV_MAX 30 /* last valid device type */
82 struct dstoc {
83 int cnt;
84 char pkg[NON_ABI_NAMELNGTH];
85 int nparts;
86 long maxsiz;
87 char volnos[128];
88 struct dstoc *next;
89 } *ds_head, *ds_toc;
91 #define ds_nparts ds_toc->nparts
92 #define ds_maxsiz ds_toc->maxsiz
94 int ds_totread; /* total number of parts read */
95 int ds_fd = -1;
96 int ds_curpartcnt = -1;
98 int ds_next(char *device, char *instdir);
99 int ds_ginit(char *device);
100 int ds_close(int pkgendflg);
102 static FILE *ds_pp;
103 static int ds_realfd = -1; /* file descriptor for real device */
104 static int ds_read; /* number of parts read for current package */
105 static int ds_volno; /* volume number of current volume */
106 static int ds_volcnt; /* total number of volumes */
107 static char ds_volnos[128]; /* parts/volume info */
108 static char *ds_device;
109 static int ds_volpart; /* number of parts read in current volume, */
110 /* including skipped parts */
111 static int ds_bufsize;
112 static int ds_skippart; /* number of parts skipped in current volume */
114 static int ds_getnextvol(char *device);
115 static int ds_skip(char *device, int nskip);
117 void
118 ds_order(char *list[])
120 struct dstoc *toc_pt;
121 register int j, n;
122 char *pt;
124 toc_pt = ds_head;
125 n = 0;
126 while (toc_pt) {
127 for (j = n; list[j]; j++) {
128 if (strcmp(list[j], toc_pt->pkg) == 0) {
129 /* just swap places in the array */
130 pt = list[n];
131 list[n++] = list[j];
132 list[j] = pt;
135 toc_pt = toc_pt->next;
139 static char *pds_header;
140 static char *ds_header;
141 static int ds_headsize;
143 static char *
144 ds_gets(char *buf, int size)
146 int length;
147 char *nextp;
149 nextp = strchr(pds_header, '\n');
150 if (nextp == NULL) {
151 length = strlen(pds_header);
152 if (length > size)
153 return (0);
154 if ((ds_header = (char *)realloc(ds_header,
155 ds_headsize + BLK_SIZE)) == NULL)
156 return (0);
157 if (read(ds_fd, ds_header + ds_headsize, BLK_SIZE) < BLK_SIZE)
158 return (0);
159 ds_headsize += BLK_SIZE;
160 nextp = strchr(pds_header, '\n');
161 if (nextp == NULL)
162 return (0);
163 *nextp = '\0';
164 if (length + (int)strlen(pds_header) > size)
165 return (0);
166 (void) strncpy(buf + length, pds_header, strlen(pds_header));
167 buf[length + strlen(pds_header)] = '\0';
168 pds_header = nextp + 1;
169 return (buf);
171 *nextp = '\0';
172 if ((int)strlen(pds_header) > size)
173 return (0);
174 (void) strncpy(buf, pds_header, strlen(pds_header));
175 buf[strlen(pds_header)] = '\0';
176 pds_header = nextp + 1;
177 return (buf);
181 * function to determine if media is datastream or mounted
182 * floppy
185 ds_readbuf(char *device)
187 char buf[BLK_SIZE];
189 if (ds_fd >= 0)
190 (void) close(ds_fd);
191 if ((ds_fd = open(device, O_RDONLY)) >= 0 &&
192 read(ds_fd, buf, BLK_SIZE) == BLK_SIZE &&
193 strncmp(buf, HDR_PREFIX, 20) == 0) {
194 if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
195 progerr(pkg_gt(ERR_UNPACK));
196 logerr(pkg_gt(MSG_MEM));
197 (void) ds_close(0);
198 return (0);
200 (void) memcpy(ds_header, buf, BLK_SIZE);
201 ds_headsize = BLK_SIZE;
203 if (ds_ginit(device) < 0) {
204 progerr(pkg_gt(ERR_UNPACK));
205 logerr(pkg_gt(MSG_OPEN), device, errno);
206 (void) ds_close(0);
207 return (0);
209 return (1);
210 } else if (ds_fd >= 0) {
211 (void) close(ds_fd);
212 ds_fd = -1;
214 return (0);
218 * Determine how many additional volumes are needed for current package.
219 * Note: a 0 will occur as first volume number when the package begins
220 * on the next volume.
222 static int
223 ds_volsum(struct dstoc *toc)
225 int curpartcnt, volcnt;
226 char volnos[128], tmpvol[128];
227 if (toc->volnos[0]) {
228 int index, sum;
229 (void) sscanf(toc->volnos, "%d %[ 0-9]", &curpartcnt, volnos);
230 volcnt = 0;
231 sum = curpartcnt;
232 while (sum < toc->nparts && sscanf(volnos, "%d %[ 0-9]",
233 &index, tmpvol) >= 1) {
234 (void) strcpy(volnos, tmpvol);
235 volcnt++;
236 sum += index;
238 /* side effect - set number of parts read on current volume */
239 ds_volpart = index;
240 return (volcnt);
242 ds_volpart += toc->nparts;
243 return (0);
246 /* initialize ds_curpartcnt and ds_volnos */
247 static void
248 ds_pkginit(void)
250 if (ds_toc->volnos[0])
251 (void) sscanf(ds_toc->volnos, "%d %[ 0-9]", &ds_curpartcnt,
252 ds_volnos);
253 else
254 ds_curpartcnt = -1;
258 * functions to pass current package info to exec'ed program
260 void
261 ds_putinfo(char *buf, size_t sz)
263 (void) snprintf(buf, sz, "%d %d %d %d %d %d %d %d %d %d %s",
264 ds_fd, ds_realfd, ds_volcnt, ds_volno, ds_totread, ds_volpart,
265 ds_skippart, ds_bufsize, ds_toc->nparts, ds_toc->maxsiz,
266 ds_toc->volnos);
270 ds_getinfo(char *string)
272 ds_toc = (struct dstoc *)calloc(1, sizeof (struct dstoc));
273 (void) sscanf(string, "%d %d %d %d %d %d %d %d %d %d %[ 0-9]",
274 &ds_fd, &ds_realfd, &ds_volcnt, &ds_volno, &ds_totread,
275 &ds_volpart, &ds_skippart, &ds_bufsize, &ds_toc->nparts,
276 &ds_toc->maxsiz, ds_toc->volnos);
277 ds_pkginit();
278 return (ds_toc->nparts);
282 * Return true if the file descriptor (ds_fd) is open on the package stream.
284 boolean_t
285 ds_fd_open(void)
287 return (ds_fd >= 0 ? B_TRUE : B_FALSE);
291 * Read the source device. Acquire the header data and check it for validity.
294 ds_init(char *device, char **pkg, char *norewind)
296 struct dstoc *tail, *toc_pt;
297 char *ret;
298 char cmd[CMDSIZ];
299 char line[LSIZE+1];
300 int i, n, count = 0, header_size = BLK_SIZE;
302 if (!ds_header) { /* If the header hasn't been read yet */
303 if (ds_fd >= 0)
304 (void) ds_close(0);
306 /* always start with rewind device */
307 if ((ds_fd = open(device, O_RDONLY)) < 0) {
308 progerr(pkg_gt(ERR_UNPACK));
309 logerr(pkg_gt(MSG_OPEN), device, errno);
310 return (-1);
313 /* allocate room for the header equivalent to a block */
314 if ((ds_header = (char *)calloc(BLK_SIZE, 1)) == NULL) {
315 progerr(pkg_gt(ERR_UNPACK));
316 logerr(pkg_gt(MSG_MEM));
317 return (-1);
320 /* initialize the device */
321 if (ds_ginit(device) < 0) {
322 (void) ds_close(0);
323 progerr(pkg_gt(ERR_UNPACK));
324 logerr(pkg_gt(MSG_OPEN), device, errno);
325 return (-1);
328 /* read a logical block from the source device */
329 if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
330 rpterr();
331 progerr(pkg_gt(ERR_UNPACK));
332 logerr(pkg_gt(MSG_TOC));
333 (void) ds_close(0);
334 return (-1);
338 * This loop scans the medium for the start of the header.
339 * If the above read worked, we skip this. If it did't, this
340 * loop will retry the read ten times looking for the header
341 * marker string.
343 while (strncmp(ds_header, HDR_PREFIX, 20) != 0) {
344 /* only ten tries iff the device rewinds */
345 if (!norewind || count++ > 10) {
346 progerr(pkg_gt(ERR_UNPACK));
347 logerr(pkg_gt(MSG_TOC));
348 (void) ds_close(0);
349 return (-1);
352 /* read through to the last block */
353 if (count > 1)
354 while (read(ds_fd, ds_header, BLK_SIZE) > 0)
357 /* then close the device */
358 (void) ds_close(0);
360 /* and reopen it */
361 if ((ds_fd = open(norewind, O_RDONLY)) < 0) {
362 progerr(pkg_gt(ERR_UNPACK));
363 logerr(pkg_gt(MSG_OPEN), device, errno);
364 (void) free(ds_header);
365 return (-1);
368 /* initialize the device */
369 if (ds_ginit(device) < 0) {
370 (void) ds_close(0);
371 progerr(pkg_gt(ERR_UNPACK));
372 logerr(pkg_gt(MSG_OPEN), device, errno);
373 return (-1);
376 /* read the block again */
377 if (read(ds_fd, ds_header, BLK_SIZE) != BLK_SIZE) {
378 rpterr();
379 progerr(pkg_gt(ERR_UNPACK));
380 logerr(pkg_gt(MSG_TOC));
381 (void) ds_close(0);
382 return (-1);
386 /* Now keep scanning until the whole header is in place. */
387 while (strstr(ds_header, HDR_SUFFIX) == NULL) {
388 /* We need a bigger buffer */
389 if ((ds_header = (char *)realloc(ds_header,
390 header_size + BLK_SIZE)) == NULL) {
391 progerr(pkg_gt(ERR_UNPACK));
392 logerr(pkg_gt(MSG_MEM));
393 (void) ds_close(0);
394 return (1);
397 /* clear the new memory */
398 (void) memset(ds_header + header_size, '\0',
399 BLK_SIZE);
402 /* read a logical block from the source device */
403 if (read(ds_fd, ds_header + header_size, BLK_SIZE) !=
404 BLK_SIZE) {
405 rpterr();
406 progerr(pkg_gt(ERR_UNPACK));
407 logerr(pkg_gt(MSG_TOC));
408 (void) ds_close(0);
409 return (-1);
410 } else
411 header_size += BLK_SIZE; /* new size */
415 * remember rewind device for ds_close to rewind at
416 * close
418 if (count >= 1)
419 ds_device = device;
420 ds_headsize = header_size;
424 pds_header = ds_header;
426 /* read datastream table of contents */
427 ds_head = tail = (struct dstoc *)0;
428 ds_volcnt = 1;
430 while (ret = ds_gets(line, LSIZE)) {
431 if (strcmp(line, HDR_SUFFIX) == 0)
432 break;
433 if (!line[0] || line[0] == '#')
434 continue;
435 toc_pt = (struct dstoc *)calloc(1, sizeof (struct dstoc));
436 if (!toc_pt) {
437 progerr(pkg_gt(ERR_UNPACK));
438 logerr(pkg_gt(MSG_MEM));
439 ecleanup();
440 (void) free(ds_header);
441 return (-1);
443 /* LINTED E_SEC_SCANF_UNBOUNDED_COPY */
444 if (sscanf(line, "%s %d %d %[ 0-9]", toc_pt->pkg,
445 &toc_pt->nparts, &toc_pt->maxsiz, toc_pt->volnos) < 3) {
446 progerr(pkg_gt(ERR_UNPACK));
447 logerr(pkg_gt(MSG_TOC));
448 free(toc_pt);
449 (void) free(ds_header);
450 ecleanup();
451 return (-1);
453 if (tail) {
454 tail->next = toc_pt;
455 tail = toc_pt;
456 } else
457 ds_head = tail = toc_pt;
458 ds_volcnt += ds_volsum(toc_pt);
460 if (!ret) {
461 progerr(pkg_gt(ERR_UNPACK));
462 logerr(pkg_gt(MSG_TOC));
463 (void) free(ds_header);
464 return (-1);
466 (void) sighold(SIGINT);
467 (void) sigrelse(SIGINT);
468 if (!ds_head) {
469 progerr(pkg_gt(ERR_UNPACK));
470 logerr(pkg_gt(MSG_EMPTY));
471 (void) free(ds_header);
472 return (-1);
474 /* this could break, thanks to cpio command limit */
475 (void) snprintf(cmd, sizeof (cmd), "%s -icdumD -C %d",
476 CPIOPROC, (int)BLK_SIZE);
477 n = 0;
478 for (i = 0; pkg[i]; i++) {
479 if (strcmp(pkg[i], "all") == 0)
480 continue;
481 if (n == 0) {
482 (void) strlcat(cmd, " ", CMDSIZ);
483 n = 1;
485 (void) strlcat(cmd, pkg[i], CMDSIZ);
486 (void) strlcat(cmd, "'/*' ", CMDSIZ);
487 (void) strlcat(cmd, " ", CMDSIZ);
490 if (n = esystem(cmd, ds_fd, -1)) {
491 rpterr();
492 progerr(pkg_gt(ERR_UNPACK));
493 logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
494 (void) free(ds_header);
495 return (-1);
498 ds_toc = ds_head;
499 ds_totread = 0;
500 ds_volno = 1;
501 return (0);
505 ds_findpkg(char *device, char *pkg)
507 char *pkglist[2];
508 int nskip, ods_volpart;
510 if (ds_head == NULL) {
511 pkglist[0] = pkg;
512 pkglist[1] = NULL;
513 if (ds_init(device, pkglist, NULL))
514 return (-1);
517 if (!pkg || pkgnmchk(pkg, "all", 0)) {
518 progerr(pkg_gt(ERR_UNPACK));
519 logerr(pkg_gt(MSG_PKGNAME));
520 return (-1);
523 nskip = 0;
524 ds_volno = 1;
525 ds_volpart = 0;
526 ds_toc = ds_head;
527 while (ds_toc) {
528 if (strcmp(ds_toc->pkg, pkg) == 0)
529 break;
530 nskip += ds_toc->nparts;
531 ds_volno += ds_volsum(ds_toc);
532 ds_toc = ds_toc->next;
534 if (!ds_toc) {
535 progerr(pkg_gt(ERR_UNPACK));
536 logerr(pkg_gt(MSG_NOPKG), pkg);
537 return (-1);
540 ds_pkginit();
541 ds_skippart = 0;
542 if (ds_curpartcnt > 0) {
543 ods_volpart = ds_volpart;
545 * skip past archives belonging to last package on current
546 * volume
548 if (ds_volpart > 0 && ds_getnextvol(device))
549 return (-1);
550 ds_totread = nskip - ods_volpart;
551 if (ds_skip(device, ods_volpart))
552 return (-1);
553 } else if (ds_curpartcnt < 0) {
554 if (ds_skip(device, nskip - ds_totread))
555 return (-1);
556 } else
557 ds_totread = nskip;
558 ds_read = 0;
559 return (ds_nparts);
563 * Get datastream part
564 * Call for first part should be preceded by
565 * call to ds_findpkg
569 ds_getpkg(char *device, int n, char *dstdir)
571 struct statvfs64 svfsb;
572 u_longlong_t free_blocks;
574 if (ds_read >= ds_nparts)
575 return (2);
577 if (ds_read == n)
578 return (0);
579 else if ((ds_read > n) || (n > ds_nparts))
580 return (2);
582 if (ds_maxsiz > 0) {
583 if (statvfs64(".", &svfsb)) {
584 progerr(pkg_gt(ERR_UNPACK));
585 logerr(pkg_gt(MSG_STATFS), errno);
586 return (-1);
588 free_blocks = (((long)svfsb.f_frsize > 0) ?
589 howmany(svfsb.f_frsize, DEV_BSIZE) :
590 howmany(svfsb.f_bsize, DEV_BSIZE)) * svfsb.f_bfree;
591 if ((ds_maxsiz + 50) > free_blocks) {
592 progerr(pkg_gt(ERR_UNPACK));
593 logerr(pkg_gt(MSG_NOSPACE), ds_maxsiz+50, free_blocks);
594 return (-1);
597 return (ds_next(device, dstdir));
600 static int
601 ds_getnextvol(char *device)
603 char prompt[128];
604 int n;
606 if (ds_close(0))
607 return (-1);
608 (void) sprintf(prompt,
609 pkg_gt("Insert %%v %d of %d into %%p"),
610 ds_volno, ds_volcnt);
611 if (n = getvol(device, NULL, NULL, prompt))
612 return (n);
613 if ((ds_fd = open(device, O_RDONLY)) < 0)
614 return (-1);
615 if (ds_ginit(device) < 0) {
616 (void) ds_close(0);
617 return (-1);
619 ds_volpart = 0;
620 return (0);
624 * called by ds_findpkg to skip past archives for unwanted packages
625 * in current volume
627 static int
628 ds_skip(char *device, int nskip)
630 char cmd[CMDSIZ];
631 int n, onskip = nskip;
633 while (nskip--) {
634 /* skip this one */
635 (void) snprintf(cmd, sizeof (cmd),
636 "%s -ictD -C %d > /dev/null", CPIOPROC, (int)BLK_SIZE);
637 if (n = esystem(cmd, ds_fd, -1)) {
638 rpterr();
639 progerr(pkg_gt(ERR_UNPACK));
640 logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
641 nskip = onskip;
642 if (ds_volno == 1 || ds_volpart > 0)
643 return (n);
644 if (n = ds_getnextvol(device))
645 return (n);
648 ds_totread += onskip;
649 ds_volpart = onskip;
650 ds_skippart = onskip;
651 return (0);
654 /* skip to end of package if necessary */
655 void
656 ds_skiptoend(char *device)
658 if (ds_read < ds_nparts && ds_curpartcnt < 0)
659 (void) ds_skip(device, ds_nparts - ds_read);
663 ds_next(char *device, char *instdir)
665 char cmd[CMDSIZ], tmpvol[128];
666 int nparts, n, index;
668 /*CONSTCOND*/
669 while (1) {
670 if (ds_read + 1 > ds_curpartcnt && ds_curpartcnt >= 0) {
671 ds_volno++;
672 if (n = ds_getnextvol(device))
673 return (n);
674 (void) sscanf(ds_volnos, "%d %[ 0-9]", &index, tmpvol);
675 (void) strcpy(ds_volnos, tmpvol);
676 ds_curpartcnt += index;
678 (void) snprintf(cmd, sizeof (cmd), "%s -icdumD -C %d",
679 CPIOPROC, (int)BLK_SIZE);
680 if (n = esystem(cmd, ds_fd, -1)) {
681 rpterr();
682 progerr(pkg_gt(ERR_UNPACK));
683 logerr(pkg_gt(MSG_CMDFAIL), cmd, n);
685 if (ds_read == 0)
686 nparts = 0;
687 else
688 nparts = ds_toc->nparts;
689 if (n || (n = ckvolseq(instdir, ds_read + 1, nparts))) {
690 if (ds_volno == 1 || ds_volpart > ds_skippart)
691 return (-1);
693 if (n = ds_getnextvol(device))
694 return (n);
695 continue;
697 ds_read++;
698 ds_totread++;
699 ds_volpart++;
701 return (0);
703 /*NOTREACHED*/
707 * ds_ginit: Determine the device being accessed, set the buffer size,
708 * and perform any device specific initialization.
712 ds_ginit(char *device)
714 int oflag;
715 char *pbufsize, cmd[CMDSIZ];
716 int fd2, fd;
718 if ((pbufsize = devattr(device, "bufsize")) != NULL) {
719 ds_bufsize = atoi(pbufsize);
720 (void) free(pbufsize);
721 } else
722 ds_bufsize = BLK_SIZE;
723 oflag = fcntl(ds_fd, F_GETFL, 0);
725 if (ds_bufsize > BLK_SIZE) {
726 if (oflag & O_WRONLY)
727 fd = 1;
728 else
729 fd = 0;
730 fd2 = fcntl(fd, F_DUPFD, fd);
731 (void) close(fd);
732 (void) fcntl(ds_fd, F_DUPFD, fd);
733 if (fd)
734 (void) snprintf(cmd, sizeof (cmd),
735 "%s obs=%d 2>/dev/null", DDPROC, ds_bufsize);
736 else
737 (void) snprintf(cmd, sizeof (cmd),
738 "%s ibs=%d 2>/dev/null", DDPROC, ds_bufsize);
739 if ((ds_pp = popen(cmd, fd ? "w" : "r")) == NULL) {
740 progerr(pkg_gt(ERR_TRANSFER));
741 logerr(pkg_gt(MSG_POPEN), cmd, errno);
742 return (-1);
744 (void) close(fd);
745 (void) fcntl(fd2, F_DUPFD, fd);
746 (void) close(fd2);
747 ds_realfd = ds_fd;
748 ds_fd = fileno(ds_pp);
750 return (ds_bufsize);
754 ds_close(int pkgendflg)
756 int n, ret = 0;
758 if (pkgendflg) {
759 if (ds_header)
760 (void) free(ds_header);
761 ds_header = (char *)NULL;
762 ds_totread = 0;
765 if (ds_pp) {
766 (void) pclose(ds_pp);
767 ds_pp = 0;
768 (void) close(ds_realfd);
769 ds_realfd = -1;
770 ds_fd = -1;
771 } else if (ds_fd >= 0) {
772 (void) close(ds_fd);
773 ds_fd = -1;
776 if (ds_device) {
777 /* rewind device */
778 if ((n = open(ds_device, 0)) >= 0)
779 (void) close(n);
780 ds_device = NULL;
782 return (ret);