home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / ncftp-2.3.0-base.tgz / ncftp-2.3.0-base.tar / contrib / ncftp / Util.c < prev    next >
C/C++ Source or Header  |  1995-11-26  |  15KB  |  762 lines

  1. /* Util.c */
  2.  
  3. #include "Sys.h"
  4.  
  5. #ifdef HAVE_GETCWD
  6. #    ifndef HAVE_UNISTD_H
  7.         extern char *getcwd();
  8. #    endif
  9. #else
  10. #    ifdef HAVE_GETWD
  11. #        include <sys/param.h>
  12. #        ifndef MAXPATHLEN
  13. #            define MAXPATHLEN 1024
  14. #        endif
  15.         extern char *getwd(char *);
  16. #    endif
  17. #endif
  18.  
  19. #include <errno.h>
  20. #include <ctype.h>
  21.  
  22. #ifdef HAVE_LIMITS_H
  23. #    include <limits.h>
  24. #endif
  25.  
  26. #include "Util.h"
  27. #include "Main.h"
  28. #include "Bookmark.h"
  29. #include "Curses.h"
  30.  
  31. time_t gMailBoxTime;            /* last modified time of mbox */
  32. int gWarnShellBug = 0;
  33. int gMarkTrailingSpace = 0;
  34.  
  35. extern int gLoggedIn, gWinInit;
  36. extern string gOurDirectoryPath;
  37. extern longstring gRemoteCWD;
  38. extern string gActualHostName;
  39. extern long gEventNumber;
  40. extern string gHost;
  41. extern Bookmark gRmtInfo;
  42. extern UserInfo gUserInfo;
  43.  
  44. /* Read a line, and axe the end-of-line. */
  45. char *FGets(char *str, size_t size, FILE *fp)
  46. {
  47.     char *cp, *nlptr;
  48.     
  49.     cp = fgets(str, ((int) size) - 1, fp);
  50.     if (cp != NULL) {
  51.         nlptr = cp + strlen(cp) - 1;
  52.         if (*nlptr == '\n')
  53.             *nlptr = '\0';
  54.     }
  55.     return cp;
  56. }    /* FGets */
  57.  
  58.  
  59.  
  60.  
  61. /* This should only be called if the program wouldn't function
  62.  * usefully without the memory requested.
  63.  */
  64. void OutOfMemory(void)
  65. {
  66.     fprintf(stderr, "Out of memory!\n");
  67.     Exit(kExitOutOfMemory);
  68. }    /* OutOfMemory */
  69.  
  70.  
  71.  
  72. /* A way to strcat to a dynamically allocated area of memory. */
  73. char *PtrCat(char *dst, char *src)
  74. {
  75.     size_t len;
  76.  
  77.     len = strlen(dst) + strlen(src) + 1;
  78.     if ((dst = Realloc(dst, len)) == NULL)
  79.         return (NULL);
  80.     strcat(dst, src);
  81.     return (dst);
  82. }    /* PtrCat */
  83.  
  84.  
  85.  
  86. /* Extends an area of memory, then cats a '/' and a string afterward. */
  87. char *PtrCatSlash(char *dst, char *src)
  88. {
  89.     size_t dlen;
  90.     char *nu;
  91.  
  92.     while (*src == '/')
  93.         ++src;
  94.  
  95.     dlen = strlen(dst);
  96.     if (dst[dlen - 1] != '/') {
  97.         dst = PtrCat(dst, "/");
  98.         if (dst == NULL)
  99.             nu = NULL;
  100.         else
  101.             nu = PtrCat(dst, src);
  102.     } else {
  103.         nu = PtrCat(dst, src);
  104.     }
  105.     return (nu);
  106. }    /* PtrCatSlash */
  107.  
  108.  
  109.  
  110. void *Realloc(void *ptr, size_t siz)
  111. {
  112.     if (ptr == NULL)
  113.         return (void *) malloc(siz);
  114.     return ((void *) realloc(ptr, siz));
  115. }    /* Realloc */
  116.  
  117.  
  118.  
  119.  
  120.  
  121. void MakeStringPrintable(char *dst, unsigned char *src, size_t siz)
  122. {
  123.     int c;
  124.     size_t i;
  125.     int endnl, numsp;
  126.  
  127.     i = 0;
  128.     --siz;    /* Leave room for nul. */
  129.     while ((i < siz) && (*src != '\0')) {
  130.         c = *src++;
  131.         if (isprint(c) || (c == '\n') || (c == '\t')) {
  132.             *dst++ = c;
  133.             ++i;
  134.         } else if (iscntrl(c) && (c != 0x7f)) {
  135.             /* Need room for 2 characters, ^x. */
  136.             if (i < siz - 1) {
  137.                 c = c + '@';
  138.                 *dst++ = '^';
  139.                 *dst++ = c;
  140.                 i += 2;
  141.             }
  142.         } else {
  143.             /* Need room for 5 characters, \xxx.
  144.              * The fifth character will be the \0 that is written by
  145.              * sprintf, but we know we have room for that already since
  146.              * we already accounted for that above.
  147.              */
  148.             if (i < siz - 3) {
  149.                 sprintf(dst, "\\%03o", c);
  150.                 i += 4;
  151.                 dst += 4;
  152.             }
  153.         }
  154.     }
  155.     *dst-- = '\0';
  156.  
  157.     /* See if this line ended with a \n. */
  158.     endnl = 0;
  159.     if (i > 0 && *dst == '\n') {
  160.         endnl = 1;
  161.         --i;
  162.         --dst;
  163.     }
  164.  
  165.     /* The user may want to be aware if there are trailing spaces
  166.      * at the end of a line.
  167.      */
  168.     numsp = 0;
  169.     while (i > 0) {
  170.         c = *dst;
  171.         if (c != ' ')
  172.             break;
  173.         numsp++;
  174.         --i;
  175.         --dst;
  176.     }
  177.  
  178.     /* Mark trailing spaces as \x where x is a space. */
  179.     ++dst;
  180.  
  181.     if (gMarkTrailingSpace) {
  182.         while ((numsp > 0) && (i < siz)) {
  183.             *dst++ = '\\';
  184.             *dst++ = ' ';
  185.             i += 2;
  186.             --numsp;
  187.         }
  188.     }
  189.  
  190.     /* Tack the newline back onto the end of the string, if needed. */
  191.     if (endnl)
  192.         *dst++ = '\n';
  193.  
  194.     *dst = '\0';
  195. }    /* MakeStringPrintable */
  196.  
  197.  
  198.  
  199.  
  200. /* This will abbreviate a string so that it fits into max characters.
  201.  * It will use ellipses as appropriate.  Make sure the string has
  202.  * at least max + 1 characters allocated for it.
  203.  */
  204. void AbbrevStr(char *dst, char *src, size_t max, int mode)
  205. {
  206.     int len;
  207.  
  208.     len = (int) strlen(src);
  209.     if (len > max) {
  210.         if (mode == 0) {
  211.             /* ...Put ellipses at left */
  212.             strcpy(dst, "...");
  213.             Strncat(dst, src + len - max + 3, max + 1);
  214.         } else {
  215.             /* Put ellipses at right... */
  216.             Strncpy(dst, src, max + 1);
  217.             strcpy(dst + max - 3, "...");
  218.         }
  219.     } else {
  220.         Strncpy(dst, src, max + 1);
  221.     }
  222. }    /* AbbrevStr */
  223.  
  224.  
  225.  
  226.  
  227.  
  228. /* Converts any uppercase characters in the string to lowercase.
  229.  * Never would have guessed that, huh?
  230.  */
  231. void StrLCase(char *dst)
  232. {
  233.     register char *cp;
  234.  
  235.     for (cp=dst; *cp != '\0'; cp++)
  236.         if (isupper((int) *cp))
  237.             *cp = (char) tolower(*cp);
  238. }    /* StrLCase */
  239.  
  240.  
  241.  
  242.  
  243. /* Use getcwd/getwd to get the full path of the current local
  244.  * working directory.
  245.  */
  246. char *GetCWD(char *buf, size_t size)
  247. {
  248. #ifdef HAVE_GETCWD
  249.     static char *cwdBuf = NULL;
  250.     static size_t cwdBufSize = 0;
  251.  
  252.     if (cwdBufSize == 0) {
  253.         cwdBufSize = (size_t) 128;
  254.         cwdBuf = (char *) malloc(cwdBufSize);
  255.     }
  256.  
  257.     for (errno = 0; ; ) {
  258.         if (cwdBuf == NULL) {
  259.             Error(kDoPerror, "Not enough free memory to get the local working directory path.\n");
  260.             (void) Strncpy(buf, ".", size);
  261.             return NULL;
  262.         }
  263.  
  264.         if (getcwd(cwdBuf, cwdBufSize) != NULL)
  265.             break;
  266.         if (errno != ERANGE) {
  267.             Error(kDoPerror, "Can't get the local working directory path.\n");
  268.             (void) Strncpy(buf, ".", size);
  269.             return NULL;
  270.         }
  271.         cwdBufSize *= 2;
  272.         cwdBuf = Realloc(cwdBuf, cwdBufSize);
  273.     }
  274.     
  275.     return (Strncpy(buf, cwdBuf, size));
  276. #else
  277. #ifdef HAVE_GETWD
  278.     static char *cwdBuf = NULL;
  279.     char *dp;
  280.     
  281.     /* Due to the way getwd is usually implemented, it's
  282.      * important to have a buffer large enough to hold the
  283.      * whole thing.  getwd usually starts at the end of the
  284.      * buffer, and works backwards, returning you a pointer
  285.      * to the beginning of it when it finishes.
  286.      */
  287.     if (size < MAXPATHLEN) {
  288.         /* Buffer not big enough, so use a temporary one,
  289.          * and then copy the first 'size' bytes of the
  290.          * temporary buffer to your 'buf.'
  291.          */
  292.         if (cwdBuf == NULL) {
  293.             cwdBuf = (char *) malloc((size_t) MAXPATHLEN);
  294.             if (cwdBuf == NULL)
  295.                 OutOfMemory();
  296.         }
  297.         dp = cwdBuf;
  298.     } else {
  299.         /* Buffer is big enough already. */
  300.         dp = buf;
  301.     }
  302.     *dp = '\0';
  303.     if (getwd(dp) == NULL) {
  304.         /* getwd() should write the reason why in the buffer then,
  305.          * according to the man pages.
  306.          */
  307.         Error(kDontPerror, "Can't get the local working directory path. %s\n", dp);
  308.         (void) Strncpy(buf, ".", size);
  309.         return (NULL);
  310.     }
  311.     return (Strncpy(buf, dp, size));
  312.     
  313. #else
  314.     /* Not really a solution, but does anybody not have either of
  315.      * getcwd or getwd?
  316.      */
  317.     Error(kDontPerror, "Can't get the cwd path; no getwd() or getcwd().\n");
  318.     return (Strncpy(buf, ".", size));
  319. #endif
  320. #endif
  321. }   /* GetCWD */
  322.  
  323.  
  324.  
  325.  
  326. char *Path(char *dst, size_t siz, char *parent, char *fname)
  327. {
  328.     (void) Strncpy(dst, parent, siz);
  329.     (void) Strncat(dst, "/", siz);
  330.     return (Strncat(dst, fname, siz));
  331. }    /* Path */
  332.  
  333.  
  334.  
  335.  
  336. char *OurDirectoryPath(char *dst, size_t siz, char *fname)
  337. {
  338.     return (Path(dst, siz, gOurDirectoryPath, fname));
  339. }    /* OurDirectoryPath */
  340.  
  341.  
  342.  
  343.  
  344. int MkDirs(char *path)
  345. {
  346.     longstring mdCmd;
  347.     int result;
  348.     
  349.     result = 0;
  350.     if (access(path, F_OK) < 0) {
  351.         STRNCPY(mdCmd, "mkdir -p ");    /* -p is nice, but not mandatory. */ 
  352.         STRNCAT(mdCmd, path);
  353.         result = system(mdCmd);
  354.     }
  355.     return (result);
  356. }    /* MkDirs */
  357.  
  358.  
  359.  
  360.  
  361. /* Closes the file supplied, if it isn't a std stream. */
  362. void CloseFile(FILE **f)
  363. {
  364.     if (*f != NULL) {
  365.         if ((*f != stdout) && (*f != stdin) && (*f != stderr))
  366.             (void) fclose(*f);
  367.         *f = NULL;
  368.     }
  369. }    /* CloseFile */
  370.  
  371.  
  372.  
  373.  
  374. /* Returns non-zero if we are the foreground process, or 0
  375.  * if we are a background process at the time of the call.
  376.  */
  377. int InForeGround(void)
  378. {
  379. #if defined(NO_FGTEST) || !defined(HAVE_TCGETPGRP)
  380.     return (1);
  381. #else
  382. #    ifndef GETPGRP_VOID
  383. #        define GETMYPGRP (getpgrp(getpid()))
  384. #    else
  385. #        define GETMYPGRP (getpgrp())
  386. #    endif
  387.     int result, status;
  388.     static int file = -2;
  389.     static int mode = -2;
  390.  
  391.     result = 1;    
  392.     if (file == -2)
  393.         file = open("/dev/tty", O_RDONLY);
  394.     
  395.     if (file >= 0) {
  396.         status = tcgetpgrp(file);
  397.         if (status >= 0) {
  398.             result = (status == GETMYPGRP);
  399.             if (mode != result) {
  400.                 if (mode == 0) {
  401.                     TraceMsg("In background.\n");
  402.                 } else
  403.                     TraceMsg("In foreground.\n");
  404.             }
  405.             mode = result;
  406.         } else if (mode == -2) {
  407.             TraceMsg("Foreground check failed.\n");
  408.             mode = 0;
  409.         }
  410.     }
  411.     return (result);
  412. #endif
  413. }    /* InForeGround */
  414.  
  415.  
  416.  
  417.  
  418. /* Returns non-zero if it appears the user is still live at the
  419.  * terminal.
  420.  */
  421. int UserLoggedIn(void)
  422. {
  423.     static int inited = 0;
  424.     static int parent_pid, stderr_was_tty;
  425.  
  426.     if (!inited) {
  427.         stderr_was_tty = isatty(2);
  428.         parent_pid = getppid();
  429.         inited++;
  430.     }
  431.     if ((stderr_was_tty && !isatty(2)) || (getppid() != parent_pid))
  432.         return 0;
  433.     return 1;
  434. }    /* UserLoggedIn */
  435.  
  436.  
  437.  
  438.  
  439. int CheckNewMail(void)
  440. {
  441.     struct stat stbuf;
  442.  
  443.     if (*gUserInfo.mail == '\0')
  444.         return 0;
  445.  
  446.     if (stat(gUserInfo.mail, &stbuf) < 0) {
  447.         /* Can't find mail_path so we'll never check it again */
  448.         *gUserInfo.mail = '\0';    
  449.         return 0;
  450.     }
  451.  
  452.     /*
  453.      * Check if the size is non-zero and the access time is less than
  454.      * the modify time -- this indicates unread mail.
  455.      */
  456.     if ((stbuf.st_size != 0) && (stbuf.st_atime <= stbuf.st_mtime)) {
  457.         if (stbuf.st_mtime > gMailBoxTime) {
  458.             (void) PrintF("You have new mail.\n");
  459.             gMailBoxTime = stbuf.st_mtime;
  460.         }
  461.         return 1;
  462.     }
  463.  
  464.     return 0;
  465. }    /* CheckNewMail */
  466.  
  467.  
  468.  
  469.  
  470.  
  471. size_t FlagStrCopy(char *dst, size_t siz, char *src)
  472. {
  473.     time_t now;
  474.     register char *p, *q;
  475.     int    flagType;
  476.     int chType;
  477.     int nextCh;
  478.     int nPercents;
  479.     int extraChar;
  480.     size_t maxSize;
  481.     size_t onScreenLen;
  482.     size_t len;
  483.     string tmpStr;
  484.     char *copy;
  485.  
  486.     nPercents = 0;
  487.     onScreenLen = 0;
  488.     extraChar = 0;
  489.     siz -= 2;        /* Need room for nul, and extra char. */
  490.     maxSize = siz;
  491.  
  492.     for (p = src, q = dst, *q = 0; *p != '\0'; p++) {
  493.         chType = *p;
  494.         switch (chType) {
  495.             case '%':
  496.                 nPercents++;
  497.                 goto copyChar;
  498.             case '@':
  499.                 flagType = *++p;
  500.                 nextCh = p[1];
  501.                 switch (flagType) {
  502.                     case '\0':
  503.                         goto done;
  504.                         break;
  505.                     case 'Z':
  506.                         /* Tell caller not to echo a final newline. */
  507.                         extraChar = '@';
  508.                         break;
  509.                     case 'M':
  510.                         if (CheckNewMail() > 0) {
  511.                             copy = "(Mail)";
  512.                             goto copyVisStr;
  513.                         }
  514.                         goto copyNothing;
  515.  
  516.                     case 'n':
  517.                         if (gLoggedIn) {
  518.                             copy = gRmtInfo.bookmarkName;
  519.                             goto copyVisStr;
  520.                         }
  521.                         goto copyNothing;
  522.                         
  523.                     case 'N':
  524.                         copy = "\n";
  525.                         goto copyVisStr;
  526.                         break;
  527.     
  528.                     /* Probably won't implement these. */
  529.                     case 'P':    /* reset to no bold, no uline, no inverse, etc. */
  530.                         /* copy = "plain...";
  531.                         goto copyInvisStr; */
  532.                         break;
  533.                     case 'B':    /* toggle boldface */
  534.                         break;
  535.                     case 'U':    /* toggle underline */
  536.                         break;
  537.                     case 'R':
  538.                     case 'I':    /* toggle inverse (reverse) video */
  539.                         break;
  540.         
  541.                     case 'D':    /* insert current directory */
  542.                     case 'J':
  543.                         if (gLoggedIn) {
  544.                             if ((flagType == 'J') && (gRmtInfo.isUnix)) {
  545.                                 /* Not the whole path, just the dir name. */
  546.                                 copy = strrchr(gRemoteCWD, '/');
  547.                                 if (copy == NULL)
  548.                                     copy = gRemoteCWD;
  549.                                 else if ((copy != gRemoteCWD) && (copy[1]))
  550.                                     ++copy;
  551.                             } else {
  552.                                 copy = gRemoteCWD;
  553.                             }
  554.                             goto copyVisStr;
  555.                         }
  556.                         goto copyNothing;
  557.         
  558.                     case 'H':    /* insert name of connected host */
  559.                         if (gLoggedIn) {
  560.                             copy = gHost;
  561.                             goto copyVisStr;
  562.                         }
  563.                         goto copyNothing;
  564.         
  565.                     case 'h':    /* insert actual name of connected host */
  566.                         if (gLoggedIn) {
  567.                             copy = gActualHostName;
  568.                             goto copyVisStr;
  569.                         }
  570.                         goto copyNothing;
  571.         
  572.                     case '!':
  573.                     case 'E':    /* insert event number */
  574.                         (void) sprintf(tmpStr, "%ld", gEventNumber);
  575.                         copy = tmpStr;
  576.                         /*FALLTHROUGH*/
  577.         
  578.                     copyVisStr:
  579.                         len = strlen(copy);
  580.                         if (siz > len) {
  581.                             q = strcpy(q, copy) + len;
  582.                             siz -= len;
  583.                             if (q[-1] == '\n') {
  584.                                 onScreenLen = 0;
  585.                             } else
  586.                                 onScreenLen += len;
  587.                         }
  588.                         break;
  589.         
  590.                     copyNothing:
  591.                         if (isspace(nextCh) || (nextCh == ':'))
  592.                             ++p;    /* Don't insert next character. */
  593.                         break;
  594.  
  595.                     default:
  596.                         goto copyChar; /* just copy it; unknown switch */
  597.                 }    /* end flagType */
  598.                 break;
  599.             
  600.             default:
  601.             copyChar:
  602.                 if (siz > 0) {
  603.                     *q++ = *p;
  604.                     --siz;
  605.                     ++onScreenLen;
  606.                 }
  607.                 break;
  608.         }
  609.     }
  610.     
  611. done:
  612.     *q = '\0';
  613.  
  614. #ifdef HAVE_STRFTIME
  615.     if ((nPercents > 0) && ((copy = StrDup(dst)) != NULL)) {
  616.         /* Only strftime if the user requested it (with a %something). */
  617.         (void) time(&now);
  618.         len = strlen(dst);
  619.         onScreenLen += strftime(dst, maxSize, copy, localtime(&now));
  620.         onScreenLen -= len;
  621.         free(copy);
  622.     }
  623. #endif
  624.     if (extraChar != 0)
  625.         dst[strlen(dst) + 1] = extraChar;
  626.     return (onScreenLen);
  627. }    /* FlagStrCopy */
  628.  
  629.  
  630.  
  631.  
  632. void OverflowAdd(long *dst, long plus)
  633. {
  634. #ifdef LONG_MAX
  635.     long x;
  636.  
  637.     x = LONG_MAX - *dst;
  638.     if (x < plus)
  639.         *dst = LONG_MAX;        /* Would overflow! */
  640.     else
  641.         *dst += plus;
  642. #else
  643.     *dst += plus;
  644. #endif
  645. }    /* OverflowAdd */
  646.  
  647.  
  648.  
  649.  
  650. FILE *POpen(char *cmd, char *mode, int saveScreen)
  651. {
  652.     FILE *fp;
  653.     
  654. #if 1
  655.     if ((++gWarnShellBug <= 2) && (gWinInit == 1) && (*mode == 'w') && (CURSES_SHELL_BUG == 1)) {
  656.         EPrintF("Sorry, that operation would crash the program with this OS.\n");
  657.         errno = 0;
  658.         return (NULL);
  659.     }
  660. #else
  661.     if (++gWarnShellBug == 1) {
  662.         EPrintF("Warning: the screen may not update correctly on this OS.\n\n");
  663.         sleep(2);
  664.     }
  665. #endif
  666.     if (saveScreen == 1)
  667.         SaveScreen();
  668.     fp = popen(cmd, mode);
  669.     return fp;
  670. }    /* POpen */
  671.  
  672.  
  673.  
  674.  
  675. #ifndef HAVE_MEMMOVE
  676. /* This code is derived from software contributed to Berkeley by
  677.  * Chris Torek.
  678.  */
  679.  
  680. /*
  681.  * sizeof(word) MUST BE A POWER OF TWO
  682.  * SO THAT wmask BELOW IS ALL ONES
  683.  */
  684. typedef    int word;        /* "word" used for optimal copy speed */
  685.  
  686. #define    wsize    sizeof(word)
  687. #define    wmask    (wsize - 1)
  688.  
  689. /*
  690.  * Copy a block of memory, handling overlap.
  691.  * This is the routine that actually implements
  692.  * (the portable versions of) bcopy, memcpy, and memmove.
  693.  */
  694. void *
  695. MemMove(void *dst0, void *src0, size_t length)
  696. {
  697.     register char *dst = (char *) dst0;
  698.     register const char *src = (char *) src0;
  699.     register size_t t;
  700.  
  701.     if (length == 0 || dst == src)        /* nothing to do */
  702.         return dst;
  703.  
  704.     /*
  705.      * Macros: loop-t-times; and loop-t-times, t>0
  706.      */
  707. #define    TLOOP(s) if (t) TLOOP1(s)
  708. #define    TLOOP1(s) do { s; } while (--t)
  709.  
  710.     if ((unsigned long)dst < (unsigned long)src) {
  711.         /*
  712.          * Copy forward.
  713.          */
  714.         t = (int)src;    /* only need low bits */
  715.         if ((t | (int)dst) & wmask) {
  716.             /*
  717.              * Try to align operands.  This cannot be done
  718.              * unless the low bits match.
  719.              */
  720.             if ((t ^ (int)dst) & wmask || length < wsize)
  721.                 t = length;
  722.             else
  723.                 t = wsize - (t & wmask);
  724.             length -= t;
  725.             TLOOP1(*dst++ = *src++);
  726.         }
  727.         /*
  728.          * Copy whole words, then mop up any trailing bytes.
  729.          */
  730.         t = length / wsize;
  731.         TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
  732.         t = length & wmask;
  733.         TLOOP(*dst++ = *src++);
  734.     } else {
  735.         /*
  736.          * Copy backwards.  Otherwise essentially the same.
  737.          * Alignment works as before, except that it takes
  738.          * (t&wmask) bytes to align, not wsize-(t&wmask).
  739.          */
  740.         src += length;
  741.         dst += length;
  742.         t = (int)src;
  743.         if ((t | (int)dst) & wmask) {
  744.             if ((t ^ (int)dst) & wmask || length <= wsize)
  745.                 t = length;
  746.             else
  747.                 t &= wmask;
  748.             length -= t;
  749.             TLOOP1(*--dst = *--src);
  750.         }
  751.         t = length / wsize;
  752.         TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
  753.         t = length & wmask;
  754.         TLOOP(*--dst = *--src);
  755.     }
  756.  
  757.     return(dst0);
  758. }    /* MemMove */
  759. #endif    /* ! HAVE_MEMMOVE */
  760.  
  761. /* eof */
  762.