home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 6 / AACD06.ISO / AACD / Utilities / amiCheck / Source / reportPanel.c < prev    next >
C/C++ Source or Header  |  1998-06-11  |  15KB  |  633 lines

  1. /* reportPanel.c 
  2.  *
  3.  *  defines the GUIFront characteristics of the reporting panel 
  4.  */
  5.   
  6. #include <stdio.h>  
  7. #include <intuition/gadgetclass.h>
  8. #include <clib/asl_protos.h>
  9. #include <libraries/asl.h>
  10. #include "amiCheck.h"
  11. #include "regGadget.h"
  12. #include "dataBase.h"
  13. #include "reportPanel.h"
  14. #include <string.h>
  15.  
  16. #define MINROW    5
  17. #define MAXROW    300
  18. #define MINCOL    40
  19. #define MAXCOL    300
  20.  
  21. /* prototypes */
  22. BOOL ReportHandleGadget(struct IntuiMessage *);
  23. void ReportInit(void);
  24. BOOL ReportGoodbye(struct IntuiMessage *, reportSetting *);
  25. BOOL ReportOutput(void);
  26. void ReportCompact(entryNode *, char *);
  27. void ReportNormal(entryNode *, char *, char *);
  28. void ReportVerbose(entryNode *, char *, char *, char *);
  29.  
  30. BOOL genrep;
  31. struct Window *repWin;
  32. ExtErrorData ReportExtData;
  33. GUIFront *repGUI;
  34.  
  35. reportSetting localrep;
  36.  
  37.  
  38. STRPTR replabels[] =
  39. {
  40.     "Compact",
  41.     "Normal",
  42.     "Verbose",
  43.     NULL,
  44. };
  45.  
  46. struct TagItem  repcytags[] = {
  47.     {GTCY_Labels,replabels},
  48.     {TAG_DONE},
  49. };
  50.  
  51. /* define gadgetspec */
  52. GadgetSpec REPgadgetspecs[] = 
  53. {
  54.         {CHECKBOX_KIND,0,0,{0,0,0,0,"To _File", NULL, GID_REPTOFILE,         
  55.                 PLACETEXT_RIGHT}, NULL, GS_DefaultTags},
  56.  
  57.         {CHECKBOX_KIND,0,0,{0,0,0,0,"_Pad Blank Line", NULL, GID_REPBLANK,         
  58.                 PLACETEXT_RIGHT}, NULL, GS_DefaultTags},
  59.  
  60.     {CYCLE_KIND,0,0,{0,0,0,0,"Report _Style", NULL, GID_REPSTYLE,
  61.         PLACETEXT_LEFT}, repcytags, GS_DefaultTags},
  62.  
  63.         {CHECKBOX_KIND,0,0,{0,0,0,0,"_Include Summary & Info", NULL, GID_REPSUMMARY,         
  64.                 PLACETEXT_RIGHT}, NULL, GS_DefaultTags},
  65.  
  66.         {INTEGER_KIND,0,0,{0,0,0,0,"Cols Per _Line", NULL, GID_REPCOLS,         
  67.                 PLACETEXT_LEFT}, NULL, GS_DefaultTags},
  68.  
  69.         {INTEGER_KIND,0,0,{0,0,0,0,"_Rows Per Page", NULL, GID_REPROWS,         
  70.                 PLACETEXT_LEFT},NumBorder, GS_DefaultTags},
  71.  
  72.         {BUTTON_KIND,0,0,{0,0,0,0,"_OK", NULL, GID_REPOK,         
  73.                 PLACETEXT_IN}, NULL, GS_DefaultTags},
  74.  
  75.         {BUTTON_KIND,0,0,{0,0,0,0,"_Cancel", NULL, GID_REPCANCEL,         
  76.                 PLACETEXT_IN}, NULL, GS_DefaultTags},
  77.  
  78.         {CHECKBOX_KIND,0,0,{0,0,0,0,"_Transaction State", NULL, GID_REPSTATE,         
  79.                 PLACETEXT_RIGHT}, NULL, GS_DefaultTags},
  80.  
  81. };
  82.  
  83. /* set up array of pointers to our specs */
  84. GadgetSpec *REP_ReportSpecs[] =
  85. {
  86.         &REPgadgetspecs[0],
  87.         &REPgadgetspecs[1],
  88.         &REPgadgetspecs[2],
  89.         &REPgadgetspecs[3],
  90.         &REPgadgetspecs[4],
  91.         &REPgadgetspecs[5],
  92.         &REPgadgetspecs[6],
  93.         &REPgadgetspecs[7],
  94.     &REPgadgetspecs[8],
  95.         NULL,
  96. };
  97.  
  98.  
  99. /* define the layout of this panel */
  100. ULONG REP_ReportPanel[] =
  101. {
  102.    GUIL_Flags, GUILF_PropShare | GUILF_EqualWidth,
  103.  
  104.    GUIL_HorizGroup, 2,
  105.     GUIL_Flags, GUILF_PropShare | GUILF_EqualHeight,
  106.     GUIL_FrameType, GUILFT_Recess,
  107.  
  108.     GUIL_VertGroup, 1,  
  109.         GUIL_Flags, GUILF_LabelAlign | GUILF_EqualWidth,
  110.         GUIL_GadgetSpecID, GID_REPSTYLE,
  111.         GUIL_GadgetSpecID, GID_REPCOLS,
  112.         GUIL_GadgetSpecID, GID_REPROWS,
  113.  
  114.     TAG_DONE,
  115.  
  116.     GUIL_VertGroup, 1,
  117.         GUIL_Flags, GUILF_LabelAlign,
  118.  
  119.         GUIL_GadgetSpecID, GID_REPTOFILE,
  120.         GUIL_GadgetSpecID, GID_REPSUMMARY,
  121.         GUIL_GadgetSpecID, GID_REPSTATE,
  122.         GUIL_GadgetSpecID, GID_REPBLANK,
  123.             
  124.     TAG_DONE,
  125.    
  126.     TAG_DONE,
  127.     
  128.     GUIL_HorizGroup,1,
  129.         GUIL_Flags, GUILF_EqualSize | GUILF_EqualWidth,
  130.         
  131.            GUIL_GadgetSpecID, GID_REPOK,
  132.        GUIL_GadgetSpecID, GID_REPCANCEL,
  133.            
  134.     TAG_DONE,   
  135.            
  136. TAG_DONE,
  137. };
  138.  
  139.  
  140. /****************************************************
  141. * ReportGUI ()
  142. *
  143. *    Creates the GUI for the stat panel.
  144. *****************************************************/
  145. void  ReportGUI(void)
  146. {
  147.  BOOL done = FALSE;
  148.  BOOL reply;
  149.  ULONG signal;
  150.      
  151.    AmiLock();
  152.  
  153.  
  154.         GF_SetGUIAttr(repGUI, GUI_OpenGUI, TRUE, TAG_DONE);
  155.           GF_GetGUIAttr(repGUI, GUI_Window, &repWin, TAG_DONE);
  156.  
  157.          ReportInit();
  158.  
  159.                 /* Process input events */
  160.                 while (!done)
  161.                 {
  162.                     struct IntuiMessage *imsg;
  163.  
  164.                     /* Wait for an event to occur */
  165.  
  166.                     signal = GF_Wait(guiapp,AmigaGuideSignal(agc));
  167.             if (signal & AmigaGuideSignal(agc)) {
  168.             AmiHelpMsg();
  169.             continue;
  170.             }
  171.  
  172.                     /* We only bother to listen for CLOSEWINDOW events.
  173.                      * Of course, in a real application, you would be
  174.                      * examining the Class field for IDCMP_GADGETUP
  175.                      * messages and act accordingly.
  176.                      */
  177.  
  178.                     while (imsg = GF_GetIMsg(guiapp))
  179.                     {
  180.             reply = TRUE;
  181.                         switch (imsg->Class) {
  182.                 case IDCMP_REFRESHWINDOW:
  183.                     RegRefresh(TRUE);
  184.                     break;
  185.  
  186.                                 case IDCMP_CLOSEWINDOW:
  187.                     done = TRUE;
  188.                                         break;
  189.  
  190.                 case IDCMP_GADGETUP:
  191.                     reply = FALSE;
  192.                     done=ReportHandleGadget(imsg);
  193.                     break;
  194.  
  195.                 case IDCMP_RAWKEY:
  196.                     AmiHelpKey(imsg,REPORT_PANEL);
  197.                     break;
  198.                         }
  199.                                      
  200.                               
  201.              if (reply)
  202.                 GF_ReplyIMsg(imsg);
  203.                     }
  204.                 }
  205.  
  206.    GF_SetGUIAttr(repGUI, GUI_OpenGUI, FALSE, TAG_DONE);               
  207.    if (genrep) {
  208.     GF_LockGUI(repGUI);
  209.     if (!ReportOutput())
  210.         AmiAnnounce("Printer Failure.\nOperation cancelled.");
  211.     GF_UnlockGUI(repGUI);
  212.    }
  213.    AmiUnlock();
  214. }
  215.  
  216. /***************************************************
  217. * ReportHandleGadget()
  218. *
  219. *    Handles gadget events for this panel
  220. ****************************************************/
  221. BOOL ReportHandleGadget(struct IntuiMessage *imsg)
  222. {
  223.  BOOL done = FALSE;
  224.  struct Gadget *gad = (struct Gadget *)(imsg->IAddress);
  225.  UWORD code = imsg->Code;
  226.  
  227.  switch (gad->GadgetID) {
  228.     
  229.     case GID_REPTOFILE:
  230.         localrep.tofile = code;
  231.         break;
  232.  
  233.     case GID_REPSTATE:
  234.         localrep.state = code;
  235.         break;
  236.  
  237.     case GID_REPSUMMARY:
  238.         localrep.summary = code;
  239.         break;
  240.  
  241.     case GID_REPBLANK:
  242.         localrep.padline = code;
  243.         break;
  244.  
  245.     case GID_REPSTYLE:
  246.         localrep.style = code;
  247.         break;
  248.  
  249.     case GID_REPROWS:
  250.          localrep.rows = (UWORD)((struct StringInfo*)(REP_ReportSpecs[GID_REPROWS]->gs_Gadget)->
  251.                 SpecialInfo)->LongInt;
  252.  
  253.         if (localrep.rows < MINROW || localrep.rows > MAXROW) {
  254.             DisplayBeep(repWin->WScreen);
  255.             DataAnnounce(repGUI, "Illegal rows per page");
  256.             GF_ReplyIMsg(imsg);
  257.             DataSitNSpin(REP_ReportSpecs[GID_REPROWS]->gs_Gadget,repWin);
  258.               return (done);
  259.         }
  260.         break;
  261.  
  262.     case GID_REPCOLS:
  263.          localrep.cols = (UWORD)((struct StringInfo*)(REP_ReportSpecs[GID_REPCOLS]->gs_Gadget)->
  264.                 SpecialInfo)->LongInt;
  265.  
  266.         if (localrep.cols < MINCOL || localrep.cols > MAXCOL) {
  267.             DisplayBeep(repWin->WScreen);
  268.             DataAnnounce(repGUI, "Illegal columns per line");
  269.             GF_ReplyIMsg(imsg);
  270.             DataSitNSpin(REP_ReportSpecs[GID_REPCOLS]->gs_Gadget,repWin);
  271.               return (done);
  272.         }
  273.         break;
  274.  
  275.     case GID_REPCANCEL:
  276.         done = TRUE;
  277.         break;
  278.  
  279.     case GID_REPOK:
  280.         if (ReportGoodbye(imsg, &localrep)) {
  281.             done = TRUE;    
  282.             memcpy(&reportControl,&localrep,sizeof(reportSetting));
  283.             amiChangedItems = TRUE;
  284.             genrep = TRUE;
  285.         }
  286.         else return (FALSE);
  287.         break;
  288.  }
  289.  
  290.  GF_ReplyIMsg(imsg);
  291.  return (done);
  292. }
  293.  
  294. /****************************************************
  295. * ReportInit()
  296. *
  297. *    Set up some globals
  298. *****************************************************/
  299. void ReportInit(void)
  300. {
  301.  
  302.  genrep = FALSE;
  303.  
  304.  memcpy(&localrep,&reportControl,sizeof(reportSetting));
  305.  
  306.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPTOFILE]->gs_Gadget,
  307.     GTCB_Checked,localrep.tofile,
  308.     TAG_DONE);
  309.  
  310.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPSUMMARY]->gs_Gadget,
  311.     GTCB_Checked,localrep.summary,
  312.     TAG_DONE);
  313.  
  314.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPBLANK]->gs_Gadget,
  315.     GTCB_Checked,localrep.padline,
  316.     TAG_DONE);
  317.  
  318.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPSTYLE]->gs_Gadget,
  319.         GTCY_Active,localrep.style,
  320.         TAG_DONE);
  321.  
  322.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPROWS]->gs_Gadget,
  323.             GTIN_Number, localrep.rows,
  324.             TAG_DONE); 
  325.  
  326.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPCOLS]->gs_Gadget,
  327.             GTIN_Number, localrep.cols,
  328.             TAG_DONE);
  329.  
  330.  GF_SetGadgetAttrs (repGUI, REP_ReportSpecs[GID_REPSTATE]->gs_Gadget,
  331.     GTCB_Checked,localrep.state,
  332.     TAG_DONE);
  333.  
  334. }
  335.  
  336. /****************************************************
  337. * ReportGoodbye()
  338. *
  339. *    Capture strings
  340. *****************************************************/
  341. BOOL ReportGoodbye(struct IntuiMessage *imsg, reportSetting *temp)
  342. {
  343.  UWORD val;
  344.  
  345.  val = (UWORD)((struct StringInfo*)(REP_ReportSpecs[GID_REPROWS]->gs_Gadget)->SpecialInfo)->LongInt;
  346.  if (val < MINROW || val > MAXROW) {
  347.     DisplayBeep(repWin->WScreen);
  348.     DataAnnounce(repGUI, "Illegal rows per page");
  349.     GF_ReplyIMsg(imsg);
  350.     DataSitNSpin(REP_ReportSpecs[GID_REPROWS]->gs_Gadget,repWin);
  351.     return (FALSE);
  352.  }
  353.  else temp->rows = val;
  354.  
  355.  val = (UWORD)((struct StringInfo*)(REP_ReportSpecs[GID_REPCOLS]->gs_Gadget)->SpecialInfo)->LongInt;
  356.  if (val < MINCOL || val > MAXCOL) {
  357.     DisplayBeep(repWin->WScreen);
  358.     DataAnnounce(repGUI, "Illegal columns per line");
  359.     GF_ReplyIMsg(imsg);
  360.     DataSitNSpin(REP_ReportSpecs[GID_REPCOLS]->gs_Gadget,repWin);
  361.      return (FALSE);
  362.  }
  363.  else temp->cols = val;
  364.  
  365.  return (TRUE);
  366. }
  367.  
  368.  
  369. /***********************************************************
  370. * ReportOutput()
  371. *
  372. *    Actual printing of data
  373. ************************************************************/
  374. BOOL ReportOutput(void)
  375.  char fname[100];
  376.  char tempstr[200],tempAmnt[AMNTSIZE+1];
  377.  char outstr[3][MAXCOL+1];
  378.  int r,x;
  379.  FILE *temp;
  380.  amountType zero=0;
  381.  balanceType amnt;
  382.  filterNode *filt = (filterNode *)filtered.mlh_Head;
  383.   
  384.  if (filtered.mlh_TailPred == &filtered)
  385.     return FALSE;
  386.  
  387.  if (reportControl.tofile) {
  388.     /* asl save req */
  389.     if (!(AmiGetFile(repGUI,reportASL,fname,100)))
  390.         return FALSE;
  391.  }
  392.  else  sprintf(fname,"prt:");
  393.     
  394.  if ((temp = fopen(fname,"w")) == NULL) {
  395.         AmiAnnounce("Trouble generating report.");
  396.         return FALSE;
  397.  }
  398.  
  399.  /* fill temp file up */
  400.  r = 0;
  401.  DataInitBal(DEPOSITTYPE,&zero,&amnt);
  402.  
  403.  /* include account info */
  404.  if (reportControl.summary) {
  405.     sprintf(tempstr,"\nAccount Holder: %s\n",usrAccount.holder);
  406.      if (fprintf(temp,tempstr) != strlen(tempstr)) {
  407.         fclose(temp);
  408.         return (FALSE);
  409.     }
  410.         
  411.  
  412.      sprintf(tempstr,"Institution   : %s\n",usrAccount.bank);
  413.      if (fprintf(temp,tempstr) != strlen(tempstr)) {
  414.         fclose(temp);
  415.         return (FALSE);
  416.     }
  417.  
  418.      sprintf(tempstr,"Account#      : %s\n",usrAccount.accnt);
  419.      if (fprintf(temp,tempstr) != strlen(tempstr)) {
  420.         fclose(temp);
  421.         return (FALSE);
  422.     }
  423.  
  424.      if (fprintf(temp,"\n") != strlen("\n")) {
  425.         fclose(temp);
  426.         return (FALSE);
  427.     }
  428.      r += 5;
  429.  }
  430.  
  431.  do {
  432.     /* by style, output columns */
  433.     switch (reportControl.style) {
  434.         case 0:
  435.             ReportCompact(filt->entry,outstr[0]);
  436.             break;
  437.         case 1:
  438.             ReportNormal(filt->entry,outstr[0], outstr[1]);
  439.             break;
  440.         case 2:
  441.             ReportVerbose(filt->entry,outstr[0], outstr[1], outstr[2]);
  442.             break;
  443.     }
  444.  
  445.     if (!(filt->entry->flags & VOIDED)) {
  446.         DataAddBal(filt->entry->type,&filt->entry->amount,&amnt);
  447.     }
  448.  
  449.     if (r+reportControl.style+2 >= reportControl.rows && !reportControl.tofile) {
  450.         if (fprintf(temp,"\f\n") != strlen("\f\n")) {
  451.             fclose(temp);
  452.             return (FALSE);
  453.         }
  454.         r = 1;
  455.     }
  456.  
  457.     for (x=0;x<=reportControl.style;x++) {
  458.         if (fprintf(temp,"%s",outstr[x]) != strlen(outstr[x])) {
  459.             fclose(temp);
  460.             return (FALSE);
  461.         }
  462.     }
  463.  
  464.     r += reportControl.style + 1;
  465.  
  466.     if (reportControl.padline && r < reportControl.rows-1){
  467.         if (fprintf(temp,"\n") != strlen("\n")) {
  468.             fclose(temp);
  469.             return (FALSE);
  470.         }
  471.         r++;
  472.     }
  473.     
  474.     filt = (filterNode *)DataGetNext((struct List *)&filtered,(struct Node *)filt);
  475.  } while (filt != NULL);
  476.  
  477.  /* fill in summary */ 
  478.  if (reportControl.summary) {
  479.     if (r + 4 > reportControl.rows-1)
  480.         if (fprintf(temp,"\f\n") != strlen("\f\n")) {
  481.             fclose(temp);
  482.             return (FALSE);
  483.         }
  484.  
  485.     if (fprintf(temp,"\n") != strlen("\n")) {
  486.         fclose(temp);
  487.         return (FALSE);
  488.     }
  489.  
  490.     DataBuildBal(usrAccount.decimal,&amnt,tempAmnt);
  491.     sprintf(tempstr,"Report Balance   : %s\n",tempAmnt);
  492.     if (fprintf(temp,tempstr) != strlen(tempstr)) {
  493.         fclose(temp);
  494.         return (FALSE);
  495.     }
  496.  
  497.     DataBuildBal(usrAccount.decimal,&amntState.currAmnt,tempAmnt);
  498.      sprintf(tempstr,"Current Balance  : %s\n",tempAmnt);
  499.     if (fprintf(temp,tempstr) != strlen(tempstr)) {
  500.         fclose(temp);
  501.         return (FALSE);
  502.     }
  503.  
  504.     DataBuildBal(usrAccount.decimal,&amntState.stateAmnt,tempAmnt);
  505.     sprintf(tempstr,"Statement Balance: %s\n",tempAmnt);
  506.     if (fprintf(temp,tempstr) != strlen(tempstr)) {
  507.         fclose(temp);
  508.         return (FALSE);
  509.     }
  510.  }
  511.  
  512.  fclose(temp);
  513.  
  514.  /* output icon */
  515.  if (reportControl.tofile) 
  516.     DataAttachIcon(iconPath,"AC_Report",fname);
  517.  
  518.  return (TRUE);
  519. }
  520.  
  521. /**************************************************************
  522. * ReportCompact()
  523. *
  524. *    Fills in string for compact output
  525. **************************************************************/
  526. void ReportCompact(entryNode *en, char *line)
  527. {
  528.  char type[11];
  529.  char tempAmnt[AMNTSIZE+1];
  530.  char amnt[13];
  531.  char date[DATESIZE+1];
  532.  char name[MAXCOL]; 
  533.  char flags[7];
  534.  int x,y, namesize;
  535.  
  536.  switch (en->type) {
  537.     case CHECKTYPE:
  538.         sprintf(type,"%05d     ",en->check);
  539.         break;
  540.  
  541.      case DEPOSITTYPE:
  542.         sprintf(type,"Deposit   ");
  543.         break;
  544.  
  545.     case WITHDRAWALTYPE:
  546.         sprintf(type,"Withdrawal");
  547.         break;
  548.  
  549.  }
  550.  
  551.  if (reportControl.state) {
  552.      sprintf(flags,"[    ]");
  553.      if (en->flags & VOIDED)
  554.         flags[1]='V';
  555.      if (en->flags & TAXDEDUCT)
  556.         flags[2]='T';
  557.      if (en->flags & CLEARED)
  558.         flags[3]='C';
  559.     if (en->flags & RECONCILED)
  560.         flags[4]='R';
  561.  }
  562.  else flags[0] = NULL;
  563.  
  564.  DataBuildAmnt(TRUE,&en->amount,tempAmnt);
  565.  sprintf(amnt,"%c% 9s%c\n",(en->type != DEPOSITTYPE)?'(':' ',tempAmnt,
  566.                 (en->type != DEPOSITTYPE)?')':' '
  567.                 );
  568.  DataBuildDate(&en->date,date);
  569.  namesize = reportControl.cols - (strlen(type)+strlen(date)+strlen(flags)+strlen(amnt));
  570.  strncpy(name,en->name,namesize);
  571.  y = strlen(name);
  572.  if (y < namesize) {
  573.     for (x=0;x< (namesize-y)-1; x++)
  574.         name[x+y] = '.';
  575.  
  576.     name[x+y]=NULL;
  577.  }
  578.  else name[namesize-1] = NULL;
  579.  
  580.  bzero(line,reportControl.cols);
  581.  sprintf(line,"%s %s %s%s%s",type, date, name, flags, amnt); 
  582. }
  583.  
  584. /****************************************************************************************
  585. * ReportNormal()
  586. *
  587. *    two line entries
  588. *****************************************************************************************/
  589. void ReportNormal(entryNode *en, char *line1, char *line2)
  590. {
  591.  
  592.  ReportCompact(en,line1);
  593.  bzero(line2,reportControl.cols);
  594.  
  595.  sprintf(line2,"                Memo: ");
  596.  
  597.  strncat(line2,en->memo,reportControl.cols-strlen(line2)-11 );
  598.  strcat(line2,"\n");
  599. }
  600.  
  601. /****************************************************************************************
  602. * ReportVerbose()
  603. *
  604. *    three line entries
  605. *****************************************************************************************/
  606. void ReportVerbose(entryNode *en, char *line1, char *line2, char *line3)
  607. {
  608.  char filler[30];
  609.  char cat[CATNAMESIZE];
  610.  char tempstr[MAXCOL];
  611.  int x,y;
  612.  
  613.  ReportNormal(en,line1,line2);
  614.  sprintf(filler,"            Category: ");
  615.  
  616.  bzero(cat,CATNAMESIZE);
  617.  strcpy(cat,en->category);
  618.  y = strlen(cat);
  619.  if (y < CATNAMESIZE-1) {
  620.      for (x=y;x<CATNAMESIZE-1;x++)  
  621.         cat[x]=' ';
  622.     cat[x] = NULL;
  623.  }
  624.  
  625.  
  626.  sprintf(tempstr,"%s%s %s\n",filler,cat,(en->flags&RECONCILED)?"RECONCILED":"");
  627.  
  628.  bzero(line3,reportControl.cols); 
  629.  strncpy(line3,tempstr,reportControl.cols-10);
  630.  line3[strlen(line3)-1] = '\n';
  631. }
  632.