home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / panix / k95source.zip / srp-passwd.c < prev    next >
C/C++ Source or Header  |  2003-01-04  |  20KB  |  725 lines

  1. /*
  2.  * Copyright 1989 - 1994, Julianne Frances Haugh
  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. Neither the name of Julianne F. Haugh nor the names of its contributors
  14.  *    may be used to endorse or promote products derived from this software
  15.  *    without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
  18.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20.  * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
  21.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27.  * SUCH DAMAGE.
  28.  */
  29.  
  30. #ifdef _WIN32
  31. #include <windows.h>            /* For WNetGetUser */
  32. #else
  33. #include <config.h>
  34. #endif
  35.  
  36. #ifndef OS2
  37. #ifndef _WIN32
  38. #include "prototypes.h"
  39. #include "defines.h"
  40. #endif
  41. #endif
  42.  
  43. #include <sys/types.h>
  44. #include <time.h>
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #ifdef OS2
  48. #ifndef _WIN32
  49. #include <io.h>
  50. #endif
  51. #endif
  52. #include <fcntl.h>
  53. #include <signal.h>
  54.  
  55. #ifndef _WIN32
  56. #ifndef OS2
  57. #include <pwd.h>
  58. #include "pwauth.h"
  59. #include "pwio.h"
  60. #include "getdef.h"
  61. #else /* OS2 */
  62. #define bzero(x,y) memset(x,0,y)
  63. #define tpw_lock() 1
  64. #define tpw_unlock() 1
  65. #define SYSLOG(x)
  66. struct passwd {
  67.         char    *pw_name;
  68.         char    *pw_passwd;
  69.         int     pw_uid;
  70.         int     pw_gid;
  71.         char    *pw_age;
  72.         char    *pw_comment;
  73.         char    *pw_gecos;
  74.         char    *pw_dir;
  75.         char    *pw_shell;
  76. };
  77. #endif /* OS2 */
  78. #else /* _WIN32 */
  79. #define bzero(x,y) memset(x,0,y)
  80. #define tpw_lock() 1
  81. #define tpw_unlock() 1
  82. #define SYSLOG(x)
  83. struct passwd {
  84.         char    *pw_name;
  85.         char    *pw_passwd;
  86.         int     pw_uid;
  87.         int     pw_gid;
  88.         char    *pw_age;
  89.         char    *pw_comment;
  90.         char    *pw_gecos;
  91.         char    *pw_dir;
  92.         char    *pw_shell;
  93. };
  94. #endif /* _WIN32 */
  95.  
  96. /* EPS STUFF */
  97.  
  98. #include "t_pwd.h"
  99. static int do_update_eps = 0;
  100. struct t_pw eps_passwd;
  101.  
  102. /*
  103.  * Global variables
  104.  */
  105.  
  106. static char *name;      /* The name of user whose password is being changed */
  107. static char myname[64]; /* The current user's name */
  108. static char *Prog;              /* Program name */
  109. static int amroot;              /* The real UID was 0 */
  110.  
  111. static int
  112.         aflg = 0,               /* -a - add user */
  113.         dflg = 0,               /* -d - delete password */
  114.         qflg = 0;               /* -q - quiet mode */
  115.  
  116. /*
  117.  * set to 1 if there are any flags which require root privileges,
  118.  * and require username to be specified
  119.  */
  120. static int anyflag = 0;
  121.  
  122. #ifdef AGING
  123. static long age_min = 0;        /* Minimum days before change   */
  124. static long age_max = 0;        /* Maximum days until change     */
  125. #endif
  126.  
  127. static int do_update_age = 0;
  128. static char crypt_passwd[128];  /* The "old-style" password, if present */
  129.  
  130. /*
  131.  * External identifiers
  132.  */
  133.  
  134. extern char *crypt_make_salt();
  135. #if !defined(__GLIBC__)
  136. extern char *l64a();
  137. #endif
  138.  
  139. extern  int     optind;         /* Index into argv[] for current option */
  140. extern  char    *optarg;        /* Pointer to current option value */
  141.  
  142. #ifdef  NDBM
  143. extern  int     sp_dbm_mode;
  144. extern  int     pw_dbm_mode;
  145. #endif
  146.  
  147. /*
  148.  * #defines for messages.  This facilities foreign language conversion
  149.  * since all messages are defined right here.
  150.  */
  151.  
  152. #define USAGE \
  153.         "usage: %s [ -a | -d | -q  ] name\n"
  154. #define ADMUSAGE \
  155.         "       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
  156. #define ADMUSAGE2 \
  157.         "       %s { -l | -u | -d | -S | -e } name\n"
  158. #define OLDPASS "Old password:"
  159. #define ROOTPASS "Enter 'root' password:"
  160. #define NEWPASSMSG \
  161. "Enter the new password (minimum of %d, maximum of %d characters)\n\
  162. Please use a combination of upper and lower case letters and numbers.\n"
  163. #define CHANGING "Changing password for %s\n"
  164. #define ADDING "Adding user %s\n"
  165. #define NEWPASS "New password:"
  166. #define NEWPASS2 "Re-enter new password:"
  167. #define WRONGPWD "Incorrect password for %s.\n"
  168. #define WRONGPWD2 "incorrect password for `%s'"
  169. #define NOMATCH "They don't match; try again.\n"
  170. #define CANTCHANGE "The password for %s cannot be changed.\n"
  171. #define CANTCHANGE2 "password locked for `%s'"
  172.  
  173. #define BADPASS "Bad password:  %s.  "
  174. #define EPSFAIL "Unable to update EPS password.\n"
  175. #define NOEPSCONF "Warning: configuration file missing; please run 'tconf'\n"
  176.  
  177. #define TOOSOON "Sorry, the password for %s cannot be changed yet.\n"
  178. #define TOOSOON2 "now < minimum age for `%s'"
  179.  
  180. #define EXECFAILED "%s: Cannot execute %s"
  181. #define EXECFAILED2 "cannot execute %s"
  182. #define WHOAREYOU "%s: Cannot determine your user name.\n"
  183. #define UNKUSER "%s: Unknown user %s\n"
  184. #define NOPERM "You may not change the password for %s.\n"
  185. #define NOPERM2 "can't change pwd for `%s'"
  186. #define UNCHANGED "The password for %s is unchanged.\n"
  187.  
  188. #define PWDBUSY "Cannot lock the password file; try again later.\n"
  189. #define OPNERROR "Cannot open the password file.\n"
  190. #define UPDERROR "Error updating the password entry.\n"
  191. #define CLSERROR "Cannot commit password file changes.\n"
  192. #define DBMERROR "Error updating the DBM password entry.\n"
  193.  
  194. #define PWDBUSY2 "can't lock password file"
  195. #define OPNERROR2 "can't open password file"
  196. #define UPDERROR2 "error updating password entry"
  197. #define CLSERROR2 "can't rewrite password file"
  198. #define DBMERROR2 "error updaring dbm password entry"
  199.  
  200. #define NOTROOT "Cannot change ID to root.\n"
  201. #define NOTROOT2 "can't setuid(0)"
  202. #define TRYAGAIN "Try again.\n"
  203. #define PASSWARN \
  204.         "\nWarning: weak password (enter it again to use it anyway).\n"
  205. #define CHANGED "Password changed.\n"
  206. #define CHGPASSWD "password for `%s' changed by user `%s'"
  207. #define NOCHGPASSWD "did not change password for `%s'"
  208. #define DELETING "Deleting password for '%s'.\n"
  209. #define DELETED "Password for '%s' deleted.\n"
  210. #define ADDED   "Password set for user '%s'\n"
  211. #define NOTADDED "Unable to add user '%s'\n"
  212.  
  213. /*
  214.  * usage - print command usage and exit
  215.  */
  216.  
  217. static void
  218. usage(status)
  219.     int status;
  220. {
  221.     fprintf(stderr, USAGE, Prog);
  222.     if (amroot) {
  223.         fprintf(stderr, ADMUSAGE, Prog);
  224.         fprintf(stderr, ADMUSAGE2, Prog);
  225.     }
  226.     exit(status);
  227. }
  228.  
  229. /*
  230.  * new_password - validate old password and replace with new
  231.  * (both old and new in global "char crypt_passwd[128]")
  232.  */
  233.  
  234. /*ARGSUSED*/
  235. static int
  236. new_password()
  237. {
  238.     char        clear[128];     /* Pointer to clear text */
  239.     char        *cipher;        /* Pointer to cipher text */
  240.     char        *cp;            /* Pointer to getpass() response */
  241.     char        orig[128];      /* Original password */
  242.     char        pass[128];      /* New password */
  243.     int i;              /* Counter for retries */
  244.     int warned;
  245.     int pass_max_len=127;
  246.     int rc = 0;
  247.  
  248.     /*
  249.      * Authenticate the user.  The user will be prompted for their
  250.      * own password.
  251.      */
  252.  
  253.     bzero(clear, 128);
  254.     bzero(orig, 128);
  255.     bzero(pass, 128);
  256.  
  257.     if (!amroot) {
  258.  
  259.         /* EPS STUFF */
  260.  
  261.         int retval;
  262.  
  263.         cipher = NULL;
  264.  
  265.         t_getpass (clear, 128, OLDPASS);
  266.         if ((retval = t_verifypw (name, clear)) > -1)
  267.         {
  268.             endtpent();
  269.             retval = (retval == 0) ? 1 : 0;
  270.         }
  271.         else
  272.         {
  273.             if (strlen (clear) > pass_max_len)
  274.             {
  275.                 memset(clear+pass_max_len, 0, strlen (clear+pass_max_len));
  276.                 clear[pass_max_len] = '\0';
  277.             }
  278.         }
  279.  
  280.         if (retval != 0)
  281.         {
  282.             SYSLOG((LOG_WARN, WRONGPWD2, name));
  283.             fprintf(stderr, WRONGPWD, name);
  284.             return -1;
  285.         }
  286.         else
  287.         {
  288.             strcpy(orig, clear);
  289.             bzero(clear, strlen (clear));
  290.             if (cipher) bzero(cipher, strlen (cipher));
  291.         }
  292.     } else {
  293.         orig[0] = '\0';
  294.     }
  295.  
  296.     /*
  297.      * Get the new password.  The user is prompted for the new password
  298.      * and has five tries to get it right.  The password will be tested
  299.      * for strength, unless it is the root user.  This provides an escape
  300.      * for initial login passwords.
  301.      */
  302.  
  303.     if (!qflg)
  304.         printf(NEWPASSMSG, 5, 127);
  305.  
  306.     warned = 0;
  307.     for (i = 5; i > 0; i--) {
  308.         t_getpass (clear, 128, NEWPASS);
  309.         cp = clear;
  310.         if (!cp) {
  311.             bzero (orig, sizeof orig);
  312.             return -1;
  313.         }
  314.         strcpy(pass, cp);
  315.         bzero(cp, strlen(cp));
  316.  
  317.         t_getpass (clear, 128, NEWPASS2);
  318.         cp = clear;
  319.         if (!cp) {
  320.             bzero (orig, sizeof orig);
  321.             bzero (clear, sizeof clear);
  322.             bzero (pass, sizeof pass);
  323.             return -1;
  324.         }
  325.         if (strcmp (cp, pass)) {
  326.             bzero (clear, sizeof clear);
  327.             bzero (pass, sizeof pass);
  328.             fprintf (stderr, NOMATCH);
  329.         } else
  330.             break;
  331.     }
  332.     bzero (orig, sizeof orig);
  333.  
  334.     if (i == 0) {
  335.         bzero (clear, sizeof clear);
  336.         bzero (pass, sizeof pass);
  337.         return -1;
  338.     }
  339.  
  340.     /*
  341.      * Encrypt the password, then wipe the cleartext password.
  342.      */
  343.  
  344.     /* EPS STUFF */
  345.     {
  346.         struct t_conf *tc;
  347.         struct t_confent *tcent;
  348.  
  349.         if ((tc = t_openconf(NULL)) == NULL ||
  350.              (tcent = t_getconflast(tc)) == NULL)
  351.         {
  352.             fprintf(stderr, NOEPSCONF);
  353.             do_update_eps = 0;
  354.             rc = -1;
  355.         }
  356.         else
  357.         {
  358.             do_update_eps = 1;
  359.             if (t_makepwent (&eps_passwd, name, pass, NULL, tcent) == NULL)
  360.                 rc = -1;
  361.         }
  362.  
  363.         if (tc) t_closeconf (tc);
  364.         pass[pass_max_len] = '\0';
  365.     }
  366.     return rc;
  367. }
  368.  
  369. /*
  370.  * check_password - test a password to see if it can be changed
  371.  *
  372.  *      check_password() sees if the invoker has permission to change the
  373.  *      password for the given user.
  374.  */
  375.  
  376. /*ARGSUSED*/
  377. static char *
  378. date_to_str(time_t t)
  379. {
  380.         static char buf[80];
  381.         struct tm *tm;
  382.  
  383.         tm = gmtime(&t);
  384.         sprintf(buf, "%02d/%02d/%02d",
  385.                 tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  386.         return buf;
  387. }
  388.  
  389. static const char *
  390. pw_status(pass)
  391.         const char *pass;
  392. {
  393.         if (*pass == '*' || *pass == '!')
  394.                 return "L";
  395.         if (*pass == '\0')
  396.                 return "NP";
  397.         return "P";
  398. }
  399.  
  400. /*
  401.  * print_status - print current password status
  402.  */
  403.  
  404. static void
  405. print_status(pw)
  406.         const struct passwd *pw;
  407. {
  408.     printf("%s %s\n", pw->pw_name, pw_status(pw->pw_passwd));
  409. }
  410.  
  411.  
  412. static void
  413. fail_exit(status)
  414.         int status;
  415. {
  416.         tpw_unlock();
  417.         exit(status);
  418. }
  419.  
  420. static void
  421. oom()
  422. {
  423.         fprintf(stderr, "%s: out of memory\n", Prog);
  424.         fail_exit(3);
  425. }
  426.  
  427. static long
  428. getnumber(str)
  429.         const char *str;
  430. {
  431.         long val;
  432.         char *cp;
  433.  
  434.         val = strtol(str, &cp, 10);
  435.         if (*cp)
  436.                 usage(6);
  437.         return val;
  438. }
  439.  
  440. /*
  441.  * passwd - change a user's password file information
  442.  *
  443.  *      This command controls the password file and commands which are
  444.  *      used to modify it.
  445.  *
  446.  *      The valid options are
  447.  *
  448.  *      -l      lock the named account (*)
  449.  *      -u      unlock the named account (*)
  450.  *      -d      delete the password for the named account (*)
  451.  *      -e      expire the password for the named account (*)
  452.  *      -x #    set sp_max to # days (*)
  453.  *      -n #    set sp_min to # days (*)
  454.  *      -w #    set sp_warn to # days (*)
  455.  *      -i #    set sp_inact to # days (*)
  456.  *      -S      show password status of named account
  457.  *      -g      execute gpasswd command to interpret flags
  458.  *      -f      execute chfn command to interpret flags
  459.  *      -s      execute chsh command to interpret flags
  460.  *      -k      change password only if expired
  461.  *
  462.  *      (*) requires root permission to execute.
  463.  *
  464.  *      All of the time fields are entered in days and converted to the
  465.  *      appropriate internal format.  For finer resolute the chage
  466.  *      command must be used.
  467.  *
  468.  *      Exit status:
  469.  *      0 - success
  470.  *      1 - permission denied
  471.  *      2 - invalid combination of options
  472.  *      3 - unexpected failure, password file unchanged
  473.  *      5 - password file busy, try again later
  474.  *      6 - invalid argument to option
  475.  */
  476.  
  477. int
  478. main(argc, argv)
  479.     int argc;
  480.     char **argv;
  481. {
  482.     char    *cp;                    /* Miscellaneous character pointing  */
  483.     int     flag;                   /* Current option to process     */
  484.     unsigned long mynamelen;
  485.     struct t_pwent pwent;
  486.     struct t_pw *  pw;
  487.  
  488.         /*
  489.          * The program behaves differently when executed by root
  490.          * than when executed by a normal user.
  491.          */
  492.  
  493. #ifdef WIN32
  494.         amroot = 0;
  495. #else
  496. #ifdef OS2
  497.         amroot = 0;
  498. #else
  499.         amroot = (getuid () == 0);
  500. #endif
  501. #endif
  502.  
  503.         /*
  504.          * Get the program name.  The program name is used as a
  505.          * prefix to most error messages.
  506.          */
  507.  
  508.         Prog = argv[0];
  509.  
  510.         /*
  511.          * The remaining arguments will be processed one by one and
  512.          * executed by this command.  The name is the last argument
  513.          * if it does not begin with a "-", otherwise the name is
  514.          * determined from the environment and must agree with the
  515.          * real UID.  Also, the UID will be checked for any commands
  516.          * which are restricted to root only.
  517.          */
  518.  
  519. #define FLAGS "adq"
  520.         while ((flag = getopt(argc, argv, FLAGS)) != EOF) {
  521. #undef FLAGS
  522.                 switch (flag) {
  523.                 case 'q':
  524.                         qflg++;  /* ok for users */
  525.                         break;
  526.                 case 'd':
  527.                         dflg++;
  528.                         anyflag = 1;
  529.                         break;
  530.                 case 'a':
  531.                         aflg++;
  532.                         anyflag = 1;
  533.                         break;
  534.                 default:
  535.                         usage(6);
  536.                 }
  537.         }
  538.  
  539.  
  540.     if ( !ck_crypto_loaddll() ) {
  541.         fprintf(stderr, "%s: unable to load crypto dll\n",Prog);
  542.         exit(1);
  543.     }
  544.  
  545.     ck_OPENSSL_add_all_algorithms_noconf();
  546.  
  547.         /*
  548.          * Now I have to get the user name.  The name will be gotten
  549.          * from the command line if possible.  Otherwise it is figured
  550.          * out from the environment.
  551.          */
  552.         mynamelen = sizeof(myname);
  553. #ifdef WIN32
  554.         WNetGetUser( NULL, myname, &mynamelen);
  555. #endif /* WIN32 */
  556. #ifdef OS2
  557.        {
  558.            char * env = getenv("USER");
  559.            if (env) {
  560.                strncpy(myname,env,64);
  561.                myname[63] = '\0';
  562.            }
  563.        }
  564. #endif /* OS2 */
  565.         if (optind < argc)
  566.                 name = argv[optind];
  567.         else
  568.                 name = myname;
  569.  
  570.  
  571.     if (anyflag && optind >= argc)
  572.         usage(2);
  573.  
  574. #ifdef OS2
  575. #define AMROOT
  576. #else
  577. #ifdef WIN32
  578. #define AMROOT
  579. #endif
  580. #endif
  581. #ifdef AMROOT
  582.     pw= t_openpwbyname((char *)t_defaultpwd());
  583.     if ( !pw ) {
  584.         amroot = 1;
  585.         if ( !aflg || strcmp("root",name) ) {
  586.             fprintf(stderr,"Password file does not exist!!!!\n");
  587.             fprintf(stderr,"The first entry in a password file must be 'root'\n");
  588.             fprintf(stderr,"Execute '%s -a root'\n",Prog);
  589.             exit(1);
  590.         }
  591.     } else if (anyflag) {
  592.         char clear[128];
  593.         int rc;
  594.  
  595.         t_closepw(pw);
  596.  
  597.         t_getpass (clear, 128, ROOTPASS);
  598.         rc = t_verifypw ("root", clear);
  599.         if (rc > -1)
  600.             endtpent();
  601.         if ( rc == 1 )
  602.             amroot = 1;
  603.     } else
  604.         t_closepw(pw);
  605. #endif
  606.  
  607.     if (anyflag && !amroot) {
  608.         fprintf(stderr, "%s: Permission denied\n", Prog);
  609.         exit(1);
  610.     }
  611.  
  612.     if ( dflg ) {
  613.  
  614.         if ( !qflg )
  615.             printf(DELETING, name);
  616.  
  617.         pwent.name = name;
  618.         pwent.password.len = 0;
  619.         pwent.password.data = NULL;
  620.  
  621.  
  622.         /* delete the password */
  623.         if (t_deletepw (NULL, name) < 0) {
  624.             fprintf (stderr, EPSFAIL);
  625.             exit(1);
  626.         }
  627.  
  628.         if ( !qflg )
  629.             printf(DELETED, name);
  630.  
  631.         return(0);
  632.     }
  633.  
  634.     if ( aflg ) {
  635.         pw= t_openpwbyname((char *)t_defaultpwd());
  636.  
  637.         if ( !qflg )
  638.             printf(ADDING, name);
  639.  
  640.         if (pw && t_getpwbyname(pw,name)) {
  641.             t_closepw(pw);
  642.             fprintf(stderr,"user '%s' already exists in password file\n",name);
  643.             exit(1);
  644.         }
  645.         if ( pw )
  646.             t_closepw(pw);
  647.  
  648.         pwent.name = name;
  649.         pwent.password.len = 0;
  650.         pwent.password.data = NULL;
  651.  
  652.         if (new_password()) {
  653.             fprintf(stderr, NOTADDED, name);
  654.             exit(1);
  655.         }
  656.  
  657.         if ( !qflg )
  658.             printf(ADDED, name);
  659.  
  660.     }
  661.     /*
  662.      * If there are no other flags, just change the password.
  663.      */
  664.  
  665.     if (!anyflag) {
  666.         crypt_passwd[0] = '\0';
  667.  
  668.         /*
  669.          * See if the user is permitted to change the password.
  670.          * Otherwise, go ahead and set a new password.
  671.          */
  672.  
  673.         /*
  674.          * Let the user know whose password is being changed.
  675.          */
  676.         if (!qflg)
  677.             printf(CHANGING, name);
  678.  
  679. #ifdef OS2
  680.         amroot = 0;
  681. #else
  682. #ifdef NT
  683.         amroot = 0;
  684. #endif
  685. #endif
  686.  
  687.         if (new_password()) {
  688.             fprintf(stderr, UNCHANGED, name);
  689.             exit(1);
  690.         }
  691.     }
  692.  
  693. /* EPS STUFF */
  694.  
  695.     if (do_update_eps)
  696.     {
  697.         /* try and see if the file is there, else create it */
  698.  
  699.         if ((pw = t_openpwbyname((char *)t_defaultpwd())) == NULL) {
  700.             int fh = creat ((char *)t_defaultpwd(), 0600);
  701.             if (fh == -1) {
  702.                 fprintf(stderr,"Unable to open password file\n");
  703.                 fail_exit(5);
  704.             }
  705.             close(fh);
  706.         } else
  707.              t_closepw(pw);
  708.  
  709.         /* change the password */
  710.         if (t_changepw ((char *)t_defaultpwd(), &(eps_passwd.pebuf)) < 0) {
  711.             fprintf (stderr, EPSFAIL);
  712.             exit(1);
  713.         }
  714.     }
  715.     else {
  716.         fprintf (stderr, EPSFAIL);
  717.         exit(1);
  718.     }
  719.  
  720.     if (!qflg)
  721.         printf(CHANGED);
  722.  
  723.     return(0);
  724. }
  725.