|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 21644 (0x548c) Types: TextFile Names: »ppp.c«
└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0 └─⟦dc59850a2⟧ »EurOpenD22/pp5.0/pp-5.tar.Z« └─⟦e5a54fb17⟧ └─⟦this⟧ »pp-5.0/Chans/grey/ppp.c«
/* ppp.c: ppp interface routines */ # ifndef lint static char Rcsid[] = "@(#)$Header: /cs/research/pp/hubris/pp-beta/Chans/grey/RCS/ppp.c,v 5.0 90/09/20 15:48:32 pp Exp Locker: pp $"; # endif /* * $Header: /cs/research/pp/hubris/pp-beta/Chans/grey/RCS/ppp.c,v 5.0 90/09/20 15:48:32 pp Exp Locker: pp $ * * $Log: ppp.c,v $ * Revision 5.0 90/09/20 15:48:32 pp * rcsforce : 5.0 public release * */ #include "util.h" #include "ppp.h" #include "qmgr.h" #include "prm.h" #include "q.h" #include "adr.h" #include "chan.h" #include <varargs.h> #include "retcode.h" #include "dr.h" #include "ap.h" static int ppp_sd = NOTOK; static CHAN *mychan; static struct prm_vars prm; static Q_struct Qs; static ADDR *ad_sender, *ad_recip, *ad_list; static enum { jnthdr, initialise, go } state; static char *this_msg; static int naddrs = 0; static char *cur_host; static char *cur_net; static char *jnt_host; static int rox_id; static int ppp_error_status; static int data_bytes; static struct timeval data_timer; #if PP_DEBUG > PP_DEBUG_NONE static int ppp_debug = 0; static int debug_init (), debug_nextmsg (); static void pdeliverystatus (), pindividualdeliverystatus (); #endif static int ppp_initfnx (), ppp_procfnx (); static void ppp_lose (); static int ros_indication (), ros_work (), ros_result (), error (), ureject (); static void ros_advise (), acs_advise (); static void cleanup_everything (); static char *bigendian (); static char *mk_jnt (); static int assemble_jntheader (); static int fetch_data (); static int ppp_procfnx_aux (); static int lastop = NOTOK; #define INITOP 1 #define PROCOP 2 extern struct RyOperation table_QmgrChannel_Operations[]; extern char *quedfldir; int ppp_init (argc, argv) int argc; char **argv; { struct AcSAPstart acss; register struct AcSAPstart *acs = &acss; struct AcSAPindication acis; register struct AcSAPindication *aci = &acis; register struct AcSAPabort *aca = &aci -> aci_abort; struct RoSAPindication rois; register struct RoSAPindication *roi = &rois; register struct RoSAPpreject *rop = &roi -> roi_preject; register struct PSAPstart *ps = &acs -> acs_start; int result; chan_init (argv[0]); (void) chdir (quedfldir); #if PP_DEBUG > PP_DEBUG_NONE if (argc > 1 && strcmp (argv[1], "-P") == 0 && argc > 2 && strcmp(argv[2], "debug") == 0) return debug_init (); #endif if (RyDispatch (NOTOK, table_QmgrChannel_Operations, operation_Qmgr_channelInitialise, ppp_initfnx, roi) == NOTOK) { ros_advise (rop, "RyDispatch: channelInitialise"); return NOTOK; } if (RyDispatch (NOTOK, table_QmgrChannel_Operations, operation_Qmgr_processmessage, ppp_procfnx, roi) == NOTOK) { ros_advise (rop, "RyDispatch: processmessage"); return NOTOK; } if (AcInit (argc, argv, acs, aci) == NOTOK) { acs_advise (aca, "AcInit"); return NOTOK; } PP_DBG (("A-ASSOCIATE.INDICATION: <%d, %s, %s, %s, %d>", acs -> acs_sd, sprintoid (acs -> acs_context), sprintaei (&acs -> acs_callingtitle), sprintaei (&acs -> acs_calledtitle), acs -> acs_ninfo)); ppp_sd = acs -> acs_sd; result = AcAssocResponse (ppp_sd, ACS_ACCEPT, ACS_USER_NULL, NULLOID, NULLAEI, NULLPA, NULLPC, ps -> ps_defctxresult, ps -> ps_prequirements, ps -> ps_srequirements, SERIAL_NONE, ps -> ps_settings, &ps -> ps_connect, NULLPEP, 0, aci); ACSFREE (acs); if (result == NOTOK) { acs_advise (aca, "A-ASSOCIATE.RESPONSE"); return ppp_sd = NOTOK; } if (RoSetService (ppp_sd, RoPService, roi) == NOTOK) { ros_advise (rop, "set RO/PS fails"); ppp_lose (); return NOTOK; } /* try and get the init channel op */ if (ros_work (ppp_sd, INITOP) != OK) { ppp_lose (); return NOTOK; } return OK; } int ppp_getnextmessage (host, net) char **host; char **net; { int result; state = jnthdr; #if PP_DEBUG > PP_DEBUG_NONE if (ppp_debug) result = debug_nextmsg (); else #endif result = ros_work (ppp_sd, PROCOP); switch (result) { default: case NOTOK: ros_result (ppp_sd, deliverystate, rox_id); ppp_lose (); return NOTOK; case OK: if (ppp_error_status == NOTOK) { ros_result (ppp_sd, deliverystate, rox_id); return ppp_getnextmessage (host, net); } if (jnt_host) free (jnt_host); *host = jnt_host = mk_jnt(cur_host); if (jnt_host == NULLCP) return NOTOK; if (cur_net) free (cur_net); if (mychan -> ch_info == NULLCP) { PP_OPER (NULLCP, ("Channel %s has no info string", mychan -> ch_name)); return NOTOK; } *net = cur_net = strdup (mychan -> ch_info); return OK; case DONE: return DONE; } } static char *hdrbuf; int ppp_getdata (buf, len) char **buf; int *len; { if (hdrbuf) { free (hdrbuf); hdrbuf = NULLCP; } switch (state) { case jnthdr: data_bytes = 0; timer_start (&data_timer); state = initialise; return assemble_jntheader (buf, len); case initialise: case go: return fetch_data (buf, len); default: PP_LOG (LLOG_EXCEPTIONS, ("Bad state in getdata")); return NOTOK; } } static int assemble_jntheader (buf, lengthp) char **buf; int *lengthp; { ADDR *ap; char *str; int len = 0; PP_NOTICE (("Originator %s", ad_sender -> ad_r822adr)); for (ap = ad_list; ap; ap = ap -> ad_next) { if ((str = bigendian (ap -> ad_r822adr)) == NULLCP) return NOTOK; if (hdrbuf == NULL) { hdrbuf = smalloc (len = strlen (str) + 2 + 1); (void) strcpy (hdrbuf, str); (void) strcat (hdrbuf, ap -> ad_next ? ",\n" : "\n\n"); } else { hdrbuf = realloc (hdrbuf, len += strlen (str) + 2); (void) strcat (hdrbuf, str); (void) strcat (hdrbuf, ap -> ad_next ? ",\n" : "\n\n"); } } *buf = hdrbuf; *lengthp = len - 1; data_bytes += len - 1; return OK; } static char transbuf[BUFSIZ]; static char *formatdir; static int fetch_data (buf, lengthp) char **buf; int *lengthp; { char filename[BUFSIZ]; static FILE *fp; static int first; int n; *buf = ""; *lengthp = 0; if (state == initialise) { state = go; first = 1; if (qid2dir (this_msg, ad_list, TRUE, &formatdir) == NOTOK) return NOTOK; if (msg_rinit (formatdir) != RP_OK) return NOTOK; if (msg_rfile (filename) != RP_OK) { msg_rend (); return NOTOK; } if ((fp = fopen (filename, "r")) == NULL) { msg_rend (); return NOTOK; } } if (!feof(fp) && (n = fread (transbuf, 1, sizeof transbuf, fp)) > 0) { *buf = transbuf; *lengthp = n; data_bytes += n; return OK; } if (first) { first = 0; (void) strcpy (transbuf, "\n"); *buf = transbuf; *lengthp = 1; data_bytes += 1; return OK; } if (ferror (fp)) { msg_rend (); (void) fclose (fp); return NOTOK; } (void) fclose (fp); if ((n = msg_rfile (filename)) == RP_DONE){ msg_rend (); *lengthp = 0; return DONE; } if (rp_isbad (n)) { msg_rend (); return NOTOK; } if ((fp = fopen (filename, "r")) == NULL) { msg_rend (); return NOTOK; } return fetch_data (buf, lengthp); } int ppp_status (status, reason) int status; char *reason; { ADDR *ap; char buf[BUFSIZ]; int confrep = int_Qmgr_status_positiveDR; int drrep = int_Qmgr_status_negativeDR; if (status == PPP_STATUS_DONE) timer_end (&data_timer, data_bytes, "Data Transfered"); for (ap = ad_list; ap; ap = ap -> ad_next) { if (ap -> ad_resp == 0) continue; switch (status) { case PPP_STATUS_DONE: if (ap ->ad_usrreq == AD_USR_CONFIRM || ap -> ad_mtarreq == AD_MTA_CONFIRM || ap -> ad_mtarreq == AD_MTA_AUDIT_CONFIRM) { (void) sprintf (buf, "Successfully sent to %s for recipient %s", cur_host, ap -> ad_r822adr); set_1dr (&Qs, ap -> ad_no, DRR_NO_REASON, -1, buf); delivery_set (ap -> ad_no, confrep); confrep = int_Qmgr_status_successSharedDR; } else { (void) wr_ad_status (ap, AD_STAT_DONE); delivery_set (ap -> ad_no, int_Qmgr_status_success); } PP_NOTICE ((">>> Message %s transfered to %s", this_msg, cur_host)); break; case PPP_STATUS_CONNECT_FAILED: delivery_setstate (ap -> ad_no, int_Qmgr_status_mtaFailure, reason); PP_NOTICE (("Connection to %s failed [%s]", cur_host, reason ? reason : "")); break; case PPP_STATUS_PERMANENT_FAILURE: (void) sprintf (buf, "Blue book transfer to %s failed: %s", cur_host, reason); delivery_set (ap -> ad_no, drrep); drrep = int_Qmgr_status_failureSharedDR; set_1dr (&Qs, ap -> ad_no, DRR_TRANSFER_FAILURE, DRD_MTA_CONGESTION, buf); PP_NOTICE (("Transfer to %s for %s perm failed [%s]", cur_host, this_msg, buf)); break; case PPP_STATUS_TRANSIENT_FAILURE: delivery_setstate (ap -> ad_no, int_Qmgr_status_messageFailure, reason); PP_NOTICE (("Temporary failure for %s to %s [%s]", this_msg, cur_host, reason ? reason : "")); break; } } wr_q2dr (&Qs, this_msg); cleanup_everything (); rd_end (); #if PP_DEBUG > PP_DEBUG_NONE if (ppp_debug){ pdeliverystatus (deliverystate); return OK; } else #endif return ros_result (ppp_sd, deliverystate, rox_id); } void ppp_terminate () { if (ppp_sd != NOTOK) ppp_lose (); } static void ppp_lose () { struct AcSAPindication acis; struct RoSAPindication rois; register struct RoSAPindication *roi = &rois; (void) AcUAbortRequest (ppp_sd, NULLPEP, 0, &acis); (void) RyLose (ppp_sd, roi); ppp_sd = NOTOK; } static int ppp_initfnx (sd, ryo, rox, in, roi) int sd; struct RyOperation *ryo; struct RoSAPinvoke *rox; caddr_t in; struct RoSAPindication *roi; { char *p; if (rox -> rox_nolinked == 0) { PP_LOG (LLOG_EXCEPTIONS, ("RO-INVOKE.INDICATION/%d: %s, unknown linkage %d", sd, ryo -> ryo_name, rox -> rox_linkid)); return ureject (sd, ROS_IP_LINKED, rox, roi); } PP_DBG (("RO-INVOKE.INDICATION/%d: %s", sd, ryo -> ryo_name)); p = qb2str ((struct type_Qmgr_Channel *) in); if ((mychan = ch_nm2struct (p)) == NULLCHAN) { PP_LOG (LLOG_EXCEPTIONS, ("Unknown channel %s", p)); free (p); error (sd, error_Qmgr_protocol, (caddr_t) NULL, rox -> rox_id); return NOTOK; } PP_NOTICE (("Starting %s (%s)", mychan -> ch_name, mychan -> ch_show)); rename_log (mychan -> ch_name); free (p); if (RyDsResult (sd, rox -> rox_id, (caddr_t) NULL, ROS_NOPRIO, roi) == NOTOK) { ros_advise (&roi -> roi_preject, "RESULT"); return NOTOK; } lastop = INITOP; return OK; } static int ppp_procfnx (sd, ryo, rox, in, roi) int sd; struct RyOperation *ryo; struct RoSAPinvoke *rox; caddr_t in; struct RoSAPindication *roi; { struct type_Qmgr_ProcMsg *arg = (struct type_Qmgr_ProcMsg *)in; if (rox -> rox_nolinked == 0) { PP_LOG (LLOG_EXCEPTIONS, ("RO-INVOKE.INDICATION/%d: %s, unknown linkage %d", sd, ryo -> ryo_name, rox -> rox_linkid)); return ureject (sd, ROS_IP_LINKED, rox, roi); } PP_DBG (("RO-INVOKE.INDICATION/%d: %s", sd, ryo -> ryo_name)); rox_id = rox -> rox_id; lastop = PROCOP; return ppp_procfnx_aux (arg); } static int ppp_procfnx_aux (arg) struct type_Qmgr_ProcMsg *arg; { struct type_Qmgr_UserList *up; int retval; int ad_count = 0; ADDR *ap, *alp = NULL; delivery_init (arg -> users); if (this_msg) free (this_msg); this_msg = qb2str (arg -> qid); ad_sender = NULLADDR; if (ad_recip) { q_free (&Qs); prm_free (&prm); } ad_recip = NULLADDR; retval = rd_msg (this_msg, &prm, &Qs, &ad_sender, &ad_recip, &ad_count); if (rp_isbad (retval)) { PP_LOG (LLOG_EXCEPTIONS, ("local/rd_msg err: %s", this_msg)); return NOTOK; } naddrs = 0; ad_list = NULLADDR; ppp_error_status = OK; for (ap = ad_recip; ap; ap = ap -> ad_next) { for (up = arg ->users; up; up = up -> next) { if (up -> RecipientId -> parm != ap -> ad_no) continue; switch (chan_acheck (ap, mychan, ad_list == NULLADDR, &cur_host)) { case OK: break; default: continue; } break; } if (up == NULL) continue; if (ad_list == NULLADDR) ad_list = alp = (ADDR *) calloc (1, sizeof *alp); else { alp -> ad_next = (ADDR *) calloc (1, sizeof *alp); alp = alp -> ad_next; } *alp = *ap; alp -> ad_next = NULLADDR; naddrs ++; } if (ad_list == NULLADDR) { PP_LOG (LLOG_EXCEPTIONS, ("No recipients in user list")); rd_end (); ppp_error_status = NOTOK; return OK; } PP_NOTICE (("Processing msg %s to %s", this_msg, cur_host)); return OK; } static int ros_work (fd, what) int fd; int what; { int result; caddr_t out; struct RoSAPindication rois; register struct RoSAPindication *roi = &rois; register struct RoSAPpreject *rop = &roi -> roi_preject; fd_set rfds; for (;;) { PP_TRACE (("ros_work loop (%d, %d)", fd, what)); FD_ZERO (&rfds); FD_SET (fd, &rfds); xselect (fd + 1, &rfds, NULLFD, NULLFD, NOTOK); switch (result = RyWait (fd, NULLIP, &out, OK, roi)) { case NOTOK: if (rop -> rop_reason == ROS_TIMER) break; case OK: case DONE: if ((result = ros_indication (fd, roi)) != OK) return result; break; default: PP_LOG (LLOG_EXCEPTIONS, ("unknown return from RoWaitRequest=%d", result)); return NOTOK; } break; } if (lastop == what) return OK; return NOTOK; } /* \f */ static int ros_indication (sd, roi) int sd; register struct RoSAPindication *roi; { int reply, result; switch (roi -> roi_type) { case ROI_INVOKE: case ROI_RESULT: case ROI_ERROR: PP_LOG (LLOG_EXCEPTIONS, ("unexpected indication type=%d", roi -> roi_type)); return NOTOK; case ROI_UREJECT: { register struct RoSAPureject *rou = &roi -> roi_ureject; if (rou -> rou_noid) PP_LOG (LLOG_EXCEPTIONS, ("RO-REJECT-U.INDICATION/%d: %s", sd, RoErrString (rou -> rou_reason))); else PP_LOG (LLOG_EXCEPTIONS, ("RO-REJECT-U.INDICATION/%d: %s (id=%d)", sd, RoErrString (rou -> rou_reason), rou -> rou_id)); return NOTOK; } case ROI_PREJECT: { register struct RoSAPpreject *rop = &roi -> roi_preject; ros_advise (rop, "RO-REJECT-P.INDICATION"); return NOTOK; } case ROI_FINISH: { register struct AcSAPfinish *acf = &roi -> roi_finish; struct AcSAPindication acis; register struct AcSAPabort *aca = &acis.aci_abort; PP_TRACE (("A-RELEASE.INDICATION/%d: %d", sd, acf -> acf_reason)); result = AcRelResponse (sd, reply = ACS_ACCEPT, ACR_NORMAL, NULLPEP, 0, &acis); ACFFREE (acf); if (result == NOTOK) acs_advise (aca, "A-RELEASE.RESPONSE"); else if (reply != ACS_ACCEPT) break; return DONE; } /* NOTREACHED */ default: PP_LOG (LLOG_EXCEPTIONS, ("unknown indication type=%d", roi -> roi_type)); return NOTOK; } return OK; } static int ros_result (sd, val, id) int sd; struct type_Qmgr_DeliveryStatus *val; int id; { struct RoSAPindication rois; struct RoSAPindication *roi = &rois; if (val == NULL) return error (sd, error_Qmgr_congested, (caddr_t) NULL, id); if (RyDsResult (sd, id, (caddr_t) val, ROS_NOPRIO, roi) == NOTOK) { ros_advise (&roi -> roi_preject, "RESULT"); return NOTOK; } return OK; } /* \f ERRORS */ static void ros_advise (rop, event) register struct RoSAPpreject *rop; char *event; { char buffer[BUFSIZ]; if (rop -> rop_cc > 0) (void) sprintf (buffer, "[%s] %*.*s", RoErrString (rop -> rop_reason), rop -> rop_cc, rop -> rop_cc, rop -> rop_data); else (void) sprintf (buffer, "[%s]", RoErrString (rop -> rop_reason)); PP_LOG (LLOG_EXCEPTIONS, ("%s: %s", event, buffer)); if (ROS_FATAL (rop -> rop_reason)) exit (1); } /* \f */ static void acs_advise (aca, event) register struct AcSAPabort *aca; char *event; { char buffer[BUFSIZ]; if (aca -> aca_cc > 0) (void) sprintf (buffer, "[%s] %*.*s", AcErrString (aca -> aca_reason), aca -> aca_cc, aca -> aca_cc, aca -> aca_data); else (void) sprintf (buffer, "[%s]", AcErrString (aca -> aca_reason)); PP_LOG (LLOG_EXCEPTIONS, ("%s: %s (source %d)", event, buffer, aca -> aca_source)); } /* \f ERROR */ static int error (sd, err, param, id) int sd, err; caddr_t param; int id; { struct RoSAPindication rois; struct RoSAPindication *roi = &rois; if (RyDsError (sd, id, err, param, ROS_NOPRIO, roi) == NOTOK) { ros_advise (&roi -> roi_preject, "ERROR"); return NOTOK; } return OK; } static int ureject (sd, reason, rox, roi) int sd, reason; struct RoSAPinvoke *rox; struct RoSAPindication *roi; { if (RyDsUReject (sd, rox -> rox_id, reason, ROS_NOPRIO, roi) == NOTOK) { ros_advise (&roi -> roi_preject, "U-REJECT"); return NOTOK; } return OK; } static char *bigendian (addr) char *addr; { extern int ap_outtype; static char *newaddr = NULLCP; AP_ptr tree = NULLAP, group = NULLAP, name = NULLAP, local = NULLAP, domain = NULLAP, route = NULLAP; PP_NOTICE (("Recipient Address '%s'", addr)); if (newaddr) { free (newaddr); newaddr = NULLCP; } ap_outtype = AP_PARSE_733 | AP_PARSE_BIG; if (ap_s2p (addr, &tree, &group, &name, &local, &domain, &route) == (char *)NOTOK) { PP_LOG (LLOG_EXCEPTIONS, ("Unable to parse (s2p) Recipient addr %s", addr)); return NULLCP; } if ((newaddr = ap_p2s (group, name, local, domain, route)) == (char *)NOTOK) { PP_LOG (LLOG_EXCEPTIONS, ("Unable to parse (p2s) Recipient addr %s", addr)); newaddr = NULLCP; } ap_free (tree); return newaddr; } #if PP_DEBUG > PP_DEBUG_NONE static int debug_init () { char buf[BUFSIZ], *p; fprintf (stderr, "Running in debug mode\nInput channel name: "); ppp_debug = 1; (void) fflush (stderr); if (fgets (buf, sizeof buf, stdin) == NULL) return NOTOK; if (p = index (buf, '\n')) *p = '\0'; if ((mychan = ch_nm2struct (buf)) == NULLCHAN) { PP_LOG (LLOG_EXCEPTIONS, ("Unknown channel %s", p)); return NOTOK; } return OK; } static int debug_nextmsg () { char buf[BUFSIZ], *p; int num; struct type_Qmgr_ProcMsg *arg; struct type_Qmgr_UserList *temp, *tail = NULL; fprintf (stderr, "Input message name: "); (void) fflush (stderr); if (fgets (buf, sizeof buf, stdin) == NULL) return NOTOK; if (p = index (buf, '\n')) *p = '\0'; arg = (struct type_Qmgr_ProcMsg *) calloc (1, sizeof *arg); arg -> qid = str2qb (buf, strlen (buf), 1); arg -> channel = str2qb (mychan -> ch_name, strlen (mychan -> ch_name), 1); fprintf (stderr, "Input recipients terminated by -1: "); fflush (stderr); do { scanf ("%d", &num); if (num != -1) { temp = (struct type_Qmgr_UserList *) calloc (1, sizeof *temp); temp -> RecipientId = (struct type_Qmgr_RecipientId *) calloc (1, sizeof *temp->RecipientId); temp -> RecipientId -> parm = num; if (arg -> users) { tail -> next = temp; tail = temp; } else arg -> users = tail = temp; } } while (num != -1); while ((num = getchar()) != EOF && num != '\n') continue; return ppp_procfnx_aux (arg); } static void pdeliverystatus (status) struct type_Qmgr_DeliveryStatus *status; { fprintf (stderr, "Delivery status\n"); if (status == NULL) fprintf (stderr, "Complete failure\n"); else { struct type_Qmgr_DeliveryStatus *ix = status; while (ix != NULL) { pindividualdeliverystatus(ix->IndividualDeliveryStatus); ix = ix->next; } } } static void pindividualdeliverystatus(status) struct type_Qmgr_IndividualDeliveryStatus *status; { fprintf (stderr, "Recipient %d: ", status->recipient->parm); switch (status->status) { case int_Qmgr_status_success: fprintf (stderr, "success"); break; case int_Qmgr_status_successSharedDR: fprintf (stderr, "successSharedDR"); break; case int_Qmgr_status_failureSharedDR: fprintf (stderr, "failureSharedDR"); break; case int_Qmgr_status_negativeDR: fprintf (stderr, "negativeDR"); break; case int_Qmgr_status_positiveDR: fprintf (stderr, "positiveDR"); break; case int_Qmgr_status_messageFailure: fprintf (stderr, "message failure"); break; case int_Qmgr_status_mtaFailure: fprintf (stderr, "mta failure"); break; case int_Qmgr_status_mtaAndMessageFailure: fprintf (stderr, "mta and message failure"); break; default: fprintf (stderr, "unknown"); break; } putc ('\n', stderr); } #endif static void cleanup_everything () { if (hdrbuf != NULL) { free (hdrbuf); hdrbuf = NULL; } lastop = NOTOK; } static char *mk_jnt (str) char *str; { char buf1[BUFSIZ], buf2[BUFSIZ]; char *argv[30]; int argc; strcpy (buf1, str); argc = sstr2arg (buf1, 30, argv, "."); if (argc < 1) return NULLCP; strcpy (buf2, argv[--argc]); while (--argc >= 0) { strcat (buf2, "."); strcat (buf2, argv[argc]); } return strdup (buf2); }