home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / c / croutes.zip / LAR.C < prev    next >
Text File  |  1984-07-29  |  16KB  |  711 lines

  1. /*
  2.  * Lar - LU format library file maintainer
  3.  *     by Stephen C. Hemminger
  4.  *     linus!sch or sch @Mitre-Bedford MA
  5.  *
  6.  *    Lattice version T. Jennings 1 Dec 83
  7.  *
  8.  * DESCRIPTION
  9.  *    Lar is a program to manipulate CP/M LU format libraries.
  10.  *    The original CP/M library program LU is the product
  11.  *    of Gary P. Novosielski. The primary use of lar is to combine several
  12.  *    files together for upload/download to a personal computer.
  13.  *
  14.  * Usage: lar key library [files] ...
  15.  *
  16.  * Key functions are:
  17.  * u - Update, add files to library    (also creates new libraries)
  18.  * t - Table of contents
  19.  * e - Extract files from library
  20.  * p - Print files in library
  21.  * d - Delete files in library
  22.  * r - Reorganize library
  23.  *
  24.  * EXAMPLES:
  25.  * lar t foo.lbr        list all files in FOO.LBR
  26.  * lar e foo.lbr 1.c 2.c    extract files 1.c, 2.c from FOO.LBR
  27.  * lar p foo.lbr 1.c        display 1.c from FOO.LBR
  28.  * lar u foo.lbr 1.c 2.c 3.c     add or replace files in FOO.LBR
  29.  *
  30.  * When creating a new library, you will be prompted for the maximum
  31.  * number of entries it can contain. Assuming NEW.LBR doen't exist ...
  32.  * lar u new.lbr        create an empty library
  33.  * lar u new.lbr a.c,b.c,d.c    create NEW.LBR, add files.
  34.  *
  35.  * The Reorganize option causes <lbrfile>.tmp to be created, and
  36.  * the contents of the old library to be copied into it.
  37.  *
  38.  * This program is public domain software, no warranty intended or
  39.  * implied.
  40.  *
  41.  *
  42.  * PORTABILITY
  43.  *    The original version by Stephan C. Hemminger was set up for 
  44.  *    Version 7 UNIX, and was not useable as is. It has been hacked
  45.  *    to fit the Lattice C compiler for MSDOS and CP/M-86. The basic
  46.  *    problems were: fread() and fwrite() incompatibility, no 
  47.  *    text/binary differntiation problem in MSDOS, structure alignment
  48.  *    problems, Lattice vs. UNIX vs. BDS. Lattice does not gaurentee
  49.  *    structure element alignment (i.e. an int after a char is not 
  50.  *    necessarily the next byte address) and therefore there is a serious
  51.  *    kludge in the structure definition for the library.
  52.  *
  53.  *    Also, I have made random changes to the source merely to reflect
  54.  *    my programming taste; the original code was quite good. I have also
  55.  *    changed the wording of some errors, etc more in line with the current
  56.  *    flavor of messages in the micro environment. The original Verbose
  57.  *    flag option was removed, and made the default. No need to suppress
  58.  *    what few messages and text there is.
  59.  *
  60.  *    As mentioned before, ther is no problem with text or binary files;
  61.  *     they are treated identically. Control-Z characters are added to the
  62.  *    end of all files to round it up to a multiple of 128 bytes.
  63.  *
  64.  *    Note that all files are kept as multiples of 128 bytes, to be 
  65.  *    compatible with the CP/M utility LU. This may present a problem
  66.  *    with certain data files, but will be OK for text and .COM files
  67.  *    anyways, and probably most other files.
  68.  *
  69.  * NOTES ON MSDOS VERSION
  70.  *    There is an annoying problem with this program when run under MSDOS.
  71.  *    If for some reason an update should fail (disk full, not enough slots)
  72.  *    the file will not be closed, and therefore there will be orphan data
  73.  *    blocks hanging around. Do CHKDSK\F to fix this. The problem is with
  74.  *    the use of the error() function, which is called throughout and just
  75.  *    exits to DOS after reporting the error, with out closing the file.
  76.  *    Check your .LBR file after this happens; it may be bad. Do a reorganize
  77.  *    to fix if necessary.
  78.  *
  79.  *    To compensate for this, I also fixed a bug in the original version. If
  80.  *    an empty library was created, it could not be used. One had to be 
  81.  *    created with at least one file. The problem was that the directory-
  82.  *    getter function (getdir) always read 16 bytes more than the actual
  83.  *    size of the directory; this was OK if there was files in the library.
  84.  *
  85.  * * Unix is a trademark of Bell Labs.
  86.  * ** CP/M is a trademark of Digital Research.
  87.  */
  88.  
  89. #include <stdio.h>
  90. #include <ctype.h>
  91.  
  92. /* There is no tell(), but lseek() does the same thing. For some reason seek()
  93. is called lseek. Fix this minor annoyance. */
  94.  
  95. long lseek();
  96. #define tell(f) lseek(f,0L,1)
  97. #define seek lseek
  98.  
  99. /* Library file status values: */
  100.  
  101. #define ACTIVE 0
  102. #define UNUSED    0xff
  103. #define    DELETED    0xfe
  104. #define    CTRLZ    0x1a
  105.  
  106.  
  107. #define    MAXFILES 256
  108. #define    SECTOR 128
  109. #define    DSIZE (sizeof(struct ludir))
  110. #define    SLOTS_SEC (SECTOR/DSIZE)
  111. #define    equal(s1, s2) (    strcmp(s1,s2) == 0 )
  112. #define false 0
  113. #define    true 1
  114. #define    bool int
  115.  
  116. /* Globals */
  117. char *fname[MAXFILES];
  118. bool ftouched[MAXFILES];
  119.  
  120. /* This bullshit (or something like it) is necessary because the byte order
  121. of structure elements is not garenteed in Lattice C, i.e. an INT right after
  122. an CHAR is not necessarily &( CHAR + 1), i.e. there's a fill byte after the
  123. CHAR to put it on a word boundary.
  124.  
  125. This is incredibly gross, and forces proper alignment for Lattice ONLY !!! */
  126.  
  127. struct ludir {            /* internal dir. stucture, 32 bytes */
  128.     char l_l[12];        /* 12 byte filename: */
  129. #define l_stat l_l[0]        /* status of file, */
  130. #define l_name l_l[1]        /* filename, */
  131. #define l_ext l_l[9]        /* extention, */
  132.     int l_off;        /* offset in library, */
  133.     int l_len;        /* length of file, */
  134.     int l_fill[8];        /* 16 byte filler, */
  135. } ldir[MAXFILES];
  136.  
  137. int errcnt, nfiles, nslots;
  138.  
  139. char *getname(), *sprintf();
  140.  
  141. main (argc, argv)
  142. int argc;
  143. char **argv;
  144. {
  145. char *flagp;
  146. char *aname;
  147.  
  148.     if (argc < 3)
  149.         help ();
  150.     aname =    argv[2];    /* name of LBR file, */
  151.     filenames (argc, argv);
  152.  
  153.     switch (tolower(*argv[1])) {
  154.     case 'u': 
  155.         update(aname);
  156.         break;
  157.     case 't': 
  158.         table(aname);
  159.         break;
  160.     case 'e': 
  161.         extract(aname);
  162.         break;
  163.     case 'p': 
  164.         print(aname);
  165.         break;
  166.     case 'd': 
  167.         delete(aname);
  168.         break;
  169.     case 'r': 
  170.         reorg(aname);
  171.         break;
  172.         break;
  173.     default: 
  174.         help();
  175.     }
  176.     exit();
  177. }
  178.  
  179. /* print error message and exit    */
  180. help ()    {
  181.     printf ("Usage: LAR [utepdr] library [files] ...\n");
  182.     printf ("Functions are:\n\tu - Update, add files to library\n");
  183.     printf ("\tt - Table of contents\n");
  184.      printf ("\te - Extract files from library\n");
  185.     printf ("\tp - Print files in library\n");
  186.     printf ("\td - Delete files in library\n");
  187.     printf ("\tr - Reorganize library\n");
  188.     exit (1);
  189. }
  190.  
  191. error (str)
  192. char *str;
  193. {
  194.     printf ("LAR: %s\n", str);
  195.     exit (1);
  196. }
  197.  
  198. cant (name)
  199. char *name;
  200. {
  201.  
  202.     printf ("%s: File open error\n", name);
  203.     exit (1);
  204. }
  205.  
  206. /* Get file names, check for dups, and initialize */
  207.  
  208. filenames (ac, av)
  209. char **av;
  210. {
  211.  register int i, j;
  212.  
  213.     errcnt = 0;
  214.     for (i = 0; i < ac - 3; i++) {
  215.     fname[i] = av[i + 3];
  216.     ftouched[i] = false;
  217.     if (i == MAXFILES)
  218.         error ("Too many file names.");
  219.     }
  220.     fname[i] = NULL;
  221.     nfiles = i;
  222.     for (i = 0; i < nfiles; i++)
  223.     for (j = i + 1; j < nfiles; j++)
  224.         if (equal (fname[i], fname[j])) {
  225.         printf ("%s", fname[i]);
  226.         error (": duplicate file name");
  227.         }
  228. }
  229.  
  230. table (lib)
  231. char *lib;
  232. {
  233. int lfd;
  234. register int i, total;
  235. int active = 0, unused = 0, deleted = 0;
  236. char *uname;
  237.  
  238.     if ((lfd= open(lib,0x8002)) == -1)
  239.         cant (lib);
  240.  
  241.     getdir (lfd);
  242.     total =    ldir[0].l_len;
  243.     printf("Name         Index    Length (sectors)\n");
  244.     printf("Directory    %4u    %6u\n", total, total);
  245.  
  246.     for (i = 1; i < nslots;    i++)
  247.         switch(ldir[i].l_stat) {
  248.         case ACTIVE:
  249.             active++;
  250.             uname = getname(&ldir[i].l_name, &ldir[i].l_ext);
  251.             total += ldir[i].l_len;
  252.             printf ("%-12s %4u   %7u\n", uname,ldir[i].l_off,ldir[i].l_len);
  253.             break;
  254.         case UNUSED:
  255.             unused++;
  256.             break;
  257.         default:
  258.             deleted++;
  259.     }
  260.     printf("-----------------------------\n");
  261.     printf("Total sectors       %7u\n", total);
  262.     printf("\nLibrary %s has %u slots, %u deleted, %u active, %u unused\n",
  263.         lib, nslots, deleted, active, unused);
  264.     close (lfd);
  265.     not_found ();
  266. }
  267.  
  268. getdir (f)
  269. int f;
  270. {
  271. int cnt;
  272.  
  273.     seek(f,0L,0);
  274.     if (read(f,&ldir[0],DSIZE) != DSIZE)    /* read 1st entry to find */
  275.         error ("No directory\n");    /* number of slots, */
  276.  
  277.     nslots = ldir[0].l_len * SLOTS_SEC;
  278.     cnt= DSIZE * (nslots - 1);        /* already read one slot, */
  279.  
  280.     if (read(f,&ldir[1],cnt) != cnt)
  281.         error ("Can't read directory - is it a library?");
  282. }
  283.  
  284. putdir (f)
  285. int f;
  286. {
  287.  
  288.     seek(f,0L,0);
  289.     if (write(f,&ldir,nslots * DSIZE) != (nslots * DSIZE))
  290.         error ("Can't write directory - library may be botched");
  291. }
  292.  
  293. initdir    (f)
  294. int f;
  295. {
  296. register int i;
  297. int numsecs;
  298. char line[80];
  299.  
  300.     for (;;) {
  301.         cprintf ("Number of slots to allocate: ");
  302.         getstring(line);
  303.         cprintf("\r\n");
  304.         nslots = atoi (line);
  305.         if (nslots < 1)
  306.             printf ("Must have at least one!\n");
  307.         else if (nslots > MAXFILES)
  308.             printf    ("Too many slots\n");
  309.         else
  310.             break;
  311.     }
  312.  
  313.     numsecs    = nslots / SLOTS_SEC;
  314.     nslots = numsecs * SLOTS_SEC;
  315.  
  316.     for (i = 0; i < nslots;    i++) {
  317.         ldir[i].l_stat= UNUSED;
  318.         blank_fill(&ldir[i].l_name,8);
  319.         blank_fill(&ldir[i].l_ext,3);
  320.     }
  321.     ldir[0].l_stat = ACTIVE;
  322.     ldir[0].l_len= numsecs;
  323.  
  324.     putdir (f);
  325. }
  326. /* Fill    an array with blanks, no trailing null.    */
  327. blank_fill(s,n)
  328. char *s;
  329. int n;
  330. {
  331.     while (n--) *s++= ' ';
  332. }
  333. /* convert nm.ex to a Unix style string */
  334.  
  335. char *getname (nm, ex)
  336. char *nm, *ex;
  337. {
  338. static char namebuf[14];
  339. int i,j;
  340.  
  341.     for (i= 0; (i < 8) && (nm[i] != ' '); i++)
  342.         namebuf[i]= tolower(nm[i]);
  343.     j= i;
  344.     namebuf[j++]= '.';
  345.  
  346.     for (i= 0; (i < 3) && (ex[i] != ' '); i++)
  347.         namebuf[j++]= tolower(ex[i]);
  348.     namebuf[j]= '\0';
  349.  
  350.     return namebuf;
  351. }
  352.  
  353. putname (cpmname, unixname)
  354. char *cpmname, *unixname;
  355. {
  356.  
  357.     cvt_to_fcb(unixname,cpmname);
  358. }
  359.  
  360. /* filarg - check if name matches argument list */
  361. filarg (name)
  362. char *name;
  363. {
  364. register int i;
  365.  
  366.     if (nfiles <= 0)
  367.     return 1;
  368.  
  369.     for (i = 0; i < nfiles;    i++)
  370.     if (equal (name, fname[i])) {
  371.         ftouched[i] = true;
  372.         return    1;
  373.     }
  374.  
  375.     return 0;
  376. }
  377.  
  378. not_found () {
  379. register int i;
  380.  
  381.     for (i = 0; i < nfiles;    i++)
  382.     if (!ftouched[i]) {
  383.         printf ("%s: not in library.\n", fname[i]);
  384.         errcnt++;
  385.     }
  386. }
  387.  
  388. extract(name)
  389. char *name;
  390. {
  391.     getfiles(name, false);
  392. }
  393.  
  394. print(name)
  395. char *name;
  396. {
  397.     getfiles(name, true);
  398. }
  399.  
  400. getfiles (name,    pflag)
  401. char *name;
  402. bool pflag;
  403. {
  404. int lfd, ofd;
  405. register int i;
  406. char *unixname;
  407.  
  408.     if ((lfd= open(name,0x8002)) == -1)
  409.         cant (name);
  410.     getdir (lfd);
  411.  
  412.     for (i = 1; i < nslots;    i++) {
  413.     if(ldir[i].l_stat != ACTIVE)
  414.         continue;
  415.     unixname = getname (&ldir[i].l_name, &ldir[i].l_ext);
  416.     if (!filarg (unixname))
  417.         continue;
  418.     printf("Extracting %s\n", unixname);
  419.     if (pflag)
  420.         ofd= open("CON",0x8002);
  421.     else
  422.         ofd= creat(unixname,0x8002);
  423.     if (ofd == -1) {
  424.         printf (" - can't create output file\n");
  425.         errcnt++;
  426.     }
  427.     else {
  428.         seek (lfd, (long) ldir[i].l_off * SECTOR,0);
  429.         acopy (lfd, ofd, ldir[i].l_len);
  430.         if (!pflag)
  431.             close (ofd);
  432.     }
  433.     }
  434.     close (lfd);
  435.     not_found ();
  436. }
  437.  
  438. acopy (fdi, fdo, nsecs)
  439. int fdi, fdo;
  440. unsigned nsecs;
  441. {
  442. char buf[SECTOR];
  443.  
  444.     while( nsecs-- != 0) {
  445.         if (read(fdi,buf,SECTOR) != SECTOR)
  446.             error("Can't read");
  447.         if (write(fdo,buf,SECTOR) != SECTOR)
  448.             error ("write error (acopy)");
  449.     }
  450. }
  451.  
  452. update (name)
  453. char *name;
  454. {
  455. int lfd;
  456. register int i;
  457.  
  458.     if ((lfd = open (name,0x8002)) == -1) {
  459.         if ((lfd = creat(name, 2)) == -1)
  460.             cant (name);
  461.         initdir (lfd);
  462.  
  463. /* We shouldnt have to do this, but writes to the (new) file fail otherwise. */
  464.  
  465.         close(lfd);        /* close it, */
  466.         lfd= open(name,0x8002);    /* reopen it, */
  467.     } 
  468.     getdir (lfd);            /* read directory, */
  469.  
  470.     for (i = 0; (i < nfiles) && (errcnt == 0); i++)
  471.         addfil (fname[i], lfd);
  472.     if (errcnt != 0)
  473.         printf ("fatal errors - last file may be bad\n");
  474.     putdir (lfd);
  475.     close (lfd);
  476. }
  477.  
  478. addfil (name, lfd)
  479. char *name;
  480. int lfd;
  481. {
  482. int ifd;
  483. register int secoffs, numsecs;
  484. register int i;
  485.  
  486.     if ((ifd= open(name,0x8002)) == -1) {
  487.         printf ("%s: can't find to add\n",name);
  488.         errcnt++;
  489.         return;
  490.     }
  491.     for (i = 0; i < nslots;    i++) {
  492.         if (equal( getname (&ldir[i].l_name, &ldir[i].l_ext), name) ) {
  493.             printf("Updating existing file %s\n",name);
  494.             break;
  495.         }
  496.         if (ldir[i].l_stat != ACTIVE) {
  497.             printf("Adding new file %s\n",name);
  498.             break;
  499.         }
  500.     }
  501.     if (i >= nslots) {
  502.         printf("Can't add %s, library is full\n",name);
  503.         errcnt++;
  504.         return;
  505.     }
  506.  
  507.     ldir[i].l_stat = ACTIVE;
  508.     putname    (&ldir[i].l_name, name);
  509.     seek(lfd, 0L, 2);        /* append to end */
  510.     secoffs    = tell(lfd) / SECTOR;
  511.  
  512.     seek(lfd,0L,2);            /* clear write error? */
  513.  
  514.     ldir[i].l_off= secoffs;
  515.     numsecs = fcopy (ifd, lfd);
  516.     ldir[i].l_len= numsecs;
  517.     close (ifd);
  518. }
  519.  
  520. fcopy (ifd, ofd)
  521. int ifd, ofd;
  522. {
  523. int total = 0;
  524. int i,n;
  525. char sectorbuf[SECTOR];
  526.  
  527.  
  528.     while (n= read(ifd,sectorbuf,SECTOR) ) {
  529.         if (n != SECTOR) {
  530.             for (i    = n; i < SECTOR; i++)
  531.                 sectorbuf[i] = CTRLZ;
  532.         }
  533.         if (write(ofd,sectorbuf,SECTOR) != SECTOR)
  534.             error("write error (fcopy)");
  535.         ++total;
  536.     }
  537.     return total;
  538. }
  539.  
  540. delete (lname)
  541. char *lname;
  542. {
  543. int f;
  544. register int i;
  545.  
  546.     if ((f= open(lname,0x8002)) == -1)
  547.         cant (lname);
  548.  
  549.     if (nfiles <= 0)
  550.         error("delete by name only");
  551.  
  552.     getdir (f);
  553.     for (i = 0; i < nslots;    i++) {
  554.         if (!filarg ( getname (&ldir[i].l_name, &ldir[i].l_ext)))
  555.             continue;
  556.         ldir[i].l_stat = DELETED;
  557.     }
  558.  
  559.     not_found();
  560.     if (errcnt > 0)
  561.         printf ("errors - library not updated\n");
  562.     else
  563.         putdir (f);
  564.     close (f);
  565. }
  566.  
  567. reorg (name)
  568. char *name;
  569. {
  570. int olib, nlib;
  571. int oldsize;
  572. register int i, j;
  573. struct ludir odir[MAXFILES];
  574. char tmpname[SECTOR];
  575.  
  576.     for (i= 0; (i < 8) && (name[i] != '.'); i++) /* copy filename, */
  577.         tmpname[i]= name[i];        /* strip off extention, */
  578.     tmpname[i]= '\0';
  579.     strcat(tmpname,".tmp");            /* make new name, */
  580.  
  581.     if ((olib= open(name,0x8002)) == -1)
  582.         cant(name);
  583.  
  584.     if ((nlib= creat(tmpname,0x8002)) == -1)
  585.         cant(tmpname);
  586.  
  587.     getdir(olib);
  588.     printf("Old library has %d slots\n", oldsize = nslots);
  589.     for(i = 0; i < nslots ; i++)
  590.         copymem( (char    *) &odir[i], (char *) &ldir[i],
  591.             sizeof(struct ludir));
  592.     initdir(nlib);
  593.     errcnt = 0;
  594.  
  595.     for (i = j = 1; i < oldsize; i++)
  596.     if( odir[i].l_stat == ACTIVE ) {
  597.         printf("Copying: %-8.8s.%3.3s\n",&odir[i].l_name, &odir[i].l_ext);
  598.         copyentry( &odir[i], olib, &ldir[j], nlib);
  599.         if (++j >= nslots) {
  600.             errcnt++;
  601.             printf("Not enough room in new library\n");
  602.             break;
  603.         }
  604.     }
  605.  
  606.     close(olib);
  607.     putdir(nlib);
  608.     close (nlib);
  609.  
  610.     if (errcnt == 0) {
  611.         unlink(name);            /* delete orig file, */
  612.         link(tmpname,name);        /* rename it, */
  613.     } else {
  614.         printf("Errors, library not updated\n");
  615.         unlink(tmpname);
  616.     }
  617. }
  618.  
  619. copyentry( old, of, new, nf )
  620. struct ludir *old, *new;
  621. int of, nf;
  622. {
  623.  
  624.     register int secoffs, numsecs;
  625.     char buf[SECTOR];
  626.  
  627.     new->l_stat = ACTIVE;
  628.     copymem(&new->l_name, &old->l_name, 8);
  629.     copymem(&new->l_ext, &old->l_ext, 3);
  630.     seek(of, (long) (old->l_off * SECTOR), 0);
  631.     seek(nf, 0L, 2);
  632.     secoffs = tell(nf) / SECTOR;
  633.  
  634.     seek(nf,0L,2);        /* clear write error? */
  635.  
  636.     new->l_off= secoffs;
  637.     numsecs    = old->l_len;
  638.     new->l_len= numsecs;
  639.  
  640.     while(numsecs--    != 0) {
  641.         if (read(of,buf,SECTOR) != SECTOR)
  642.             error("read error");
  643.         if (write(nf,buf,SECTOR) != SECTOR)
  644.             error("write error (copyentry)");
  645.     }
  646. }
  647.  
  648. copymem(dst, src, n)
  649. register char *dst, *src;
  650. register unsigned int n;
  651. {
  652.     while(n-- != 0)
  653.         *dst++ = *src++;
  654. }
  655. /* ATOI() function missing from Lattice C. From Kernighan and Richie. */
  656.  
  657. atoi(s)
  658. char s[];
  659. {
  660. int i,n;
  661.  
  662.     n= 0;
  663.     for (i= 0; s[i] >= '0' && s[i] <= '9'; ++i) 
  664.         n= 10*n + s[i]-'0';
  665.     return(n);
  666. }
  667. /* Missing Lattice C function: File rename
  668.  
  669.     error= link(oldname,newname); */
  670.  
  671. link(old,new)
  672. char *old,*new;
  673. {
  674. char fcb[36];
  675. int i;
  676. char *p;
  677.  
  678.     for (i= 0; i < sizeof(fcb); i++)    /* clear it out first, */
  679.         fcb[i]= '\0';
  680.  
  681.     cvt_to_fcb(old,&fcb[1]);        /* first name, */
  682.     cvt_to_fcb(new,&fcb[17]);        /* new name, */
  683.     return(bdos(23,&fcb) == 0xff);        /* do it. */
  684. }
  685.  
  686. /* Convert a normal asciz string to MSDOS/CPM FCB format. Make the filename
  687. portion 8 characters, extention 3 maximum. */
  688.  
  689. cvt_to_fcb(inname,outname)
  690. char *inname;
  691. char outname[];
  692. {
  693. char c;
  694. int i;
  695.  
  696.     for (i= 0; i < 11; i++)
  697.         outname[i]= ' ';        /* clear out name, */
  698.     for (i= 0; i < 11; i++) {
  699.         if (*inname == '\0')        /* if null, */
  700.             outname[i]= ' ';    /* pad with blanks, */
  701.         else if (*inname == '.') {    /* if a dot, */
  702.             ++inname;        /* skip it, */
  703.             i= 7;            /* skip to extention, */
  704.         } else {
  705.             outname[i]= toupper(*inname);
  706.             ++inname;
  707.         }
  708.     }
  709.     return;
  710. }
  711.