home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Interactive Guide / c-cplusplus-interactive-guide.iso / c_ref / csource1 / chint / useful10.hnt < prev    next >
Encoding:
Text File  |  1993-09-29  |  9.6 KB  |  305 lines

  1. /****************************************************************************
  2.   This useful hint shows how to modify the standard, (single user), DBC.SKL
  3.   so that a whole branch of records of a database can be copied as a block.
  4.  
  5.   IE. Copy a record, and all it's children, and it's childrens children, etc.
  6.  
  7.   To aid in writing the procedure to accomplish this functionallity it is
  8.   first necessary to re-organise the skeleton so that certain functions and
  9.   routines can be made available to outside functions.
  10.  
  11. *****************************************************************************/
  12.  
  13. 1) Copy the Standard skeleton DBC.SKL to COPYBRAN.SKL.  Now make the
  14.    following modifications to COPYBRAN.SKL...
  15.  
  16. ----------------------------------------------------------------------------
  17.  
  18. 2) Certain procedures that we are going to write will need to have some
  19.    lines conditionally compiled depending on whether the database definition
  20.    uses "Memo" fields and/or make use of the automatic "Adjust" function.
  21.  
  22.    At the top of the skeleton add the following :--
  23.  
  24.         ⁿIFDEF MEMOSⁿ
  25.         #define _USESMEMOS_
  26.         ⁿENDDEFⁿ
  27.  
  28.         ⁿIFDEF ADJUSTⁿ
  29.         #define _USESADJUST_
  30.         ⁿENDDEFⁿ
  31.  
  32. ----------------------------------------------------------------------------
  33.  
  34. 3) The function "found()" and the function "getarec()" both use the same
  35.    imbedded DataBoss Macro, "getrec".  This Macro should be re-coded as a
  36.    procedure in its own right, called "getSingleRec()", and both "getarec()"
  37.    and "found()" should now call "getSingleRec()" :--
  38.  
  39.    /* Place this just before the code for the "found" function */
  40.         void getSingleRec(int fno)
  41.         {
  42.           switch (fno) {
  43.             ⁿgetrecⁿ
  44.           }
  45.         }
  46.  
  47.    /* So found() Changes To ... */
  48.         bool found(int fno, int kno, keystr chkkey)
  49.         ...
  50.         ...
  51.           ok = (bool)(ok && (strsearch(aKey, chkkey) == aKey));
  52.           if (ok) getSingleRec(fno);
  53.           tfound = ok;
  54.         ...
  55.         ...
  56.         }
  57.  
  58.    /* And getarec() changes to ... */
  59.        void getarec(int fno)
  60.        ...
  61.        ...
  62.          recavail[fno] = ok;
  63.          if (ok) getSingleRec(fno);
  64.        ⁿIFDEF LINKEDⁿ
  65.        ...
  66.        ...
  67.  
  68. ----------------------------------------------------------------------------
  69.  
  70. 4) The "add_record()" function has two imbedded DataBoss macros that we need
  71.    access to. "clearmemo" and "setfield" both need to be moved into
  72.    procedures of their own.
  73.  
  74.    Create a new function "clearMomoFields()" and add this function directly
  75.    after the "clearmemo" function, and INSIDE the "IFDEF MEMOS" DataBoss
  76.    conditional generation macro, :--
  77.  
  78.         ⁿIFDEF MEMOSⁿ
  79.         void clearmemo(ptr fb)
  80.         ...
  81.         ...
  82.         }
  83.  
  84.         void clearMemoFields(int fno)              /* <-- New */
  85.         {                                          /* <-- New */
  86.           switch (fno) {                           /* <-- New */
  87.             ⁿCLEARMEMOⁿ                            /* <-- New */
  88.           }                                        /* <-- New */
  89.         }                                          /* <-- New */
  90.         ⁿENDDEFⁿ
  91.  
  92.    Also create a new function, called "recDefaultValues()", that is based
  93.    upon the ⁿsetfieldⁿ macro.  The code for this procedure will need to go
  94.    just before "add_record()", and as it may need to call the "autoinc()"
  95.    function this will also need to be moved above both the "add_record()" and
  96.    the "recDefaultValues()" functions.
  97.  
  98.    The current "addarec()" function will also need to be modified so that
  99.    it can be passed a file number to indicate which record to add, (at the
  100.    moment it add a record based on the global "filno" variable).
  101.  
  102.         strptr autoinc(keystr sout, int kno)
  103.         {
  104.         ...
  105.         ...
  106.         }
  107.  
  108.         void recDefaultValues(int filno)      /* New */
  109.         {                                     /* New */
  110.           string tts;                         /* New */
  111.                                               /* New */
  112.           ⁿSETFIELDⁿ                          /* New */
  113.         }                                     /* New */
  114.  
  115.         void addarec(int filno)               /* <-- Modified */
  116.         {
  117.         ...
  118.         ...
  119.         }
  120.  
  121.    /* "Add_Record" can now call "ClearMemoFields" and "RecDefaultValues" */
  122.         void add_record(char func)
  123.         {
  124.         ...
  125.         ...
  126.           cleartop = (bool)(func == 'A');
  127.           clear_rec(filno);
  128.           scrn_active = True;
  129.         ⁿIFDEF MEMOSⁿ
  130.           if (!cleartop) clearMemoFields(filno);      /* <-- New */
  131.         ⁿENDDEFⁿ
  132.           cleartop = True;
  133.           recDefaultValues(filno);                    /* <-- New */
  134.         ⁿIFDEF TABLESⁿ
  135.         ...
  136.         ...
  137.           if (exitcode != QitKey) {
  138.         ⁿIFDEF MUSERⁿ
  139.           must_secure_rec(filno,0L,2);
  140.           addarec(filno);      /* <-- Modified */
  141.           tv = lock_datf(datf[filno],recno[filno],Lock);
  142.           tv = lock_datf(datf[filno],0L,UnLock);
  143.         ⁿELSEDEFⁿ
  144.           addarec(filno);      /* <-- Modified */
  145.         ⁿENDDEFⁿ
  146.           for (keyno = 1; keyno <= maxkeyno; keyno++) savkey[keyno][0] = '\0';
  147.         ...
  148.         ...
  149.         }
  150.  
  151. ----------------------------------------------------------------------------
  152.  
  153. 5) Now that these changes are in place we can write the functions that
  154.    actually copy a block of records.  You could add extra code directly
  155.    to the modified skeleton, however I would suggest you put it in a
  156.    function file, (call it COPYBRAN.FUN).
  157.  
  158. /* The contents of COPYBRAN.FUN */
  159. uchar msg_NeedHoldingFile[] = "Cannot open temporary holding file.  Copy aborted!";
  160.  
  161. typedef struct {
  162.   int fileNumber;
  163.   long recordRef;
  164. } fileAndRecRef;
  165.  
  166. FILE *hFile;
  167.  
  168. void duplicateBranch(int fno)
  169. {
  170.   fileAndRecRef addThisRec;
  171.   bool savOK, tb;
  172.   int i;
  173.   keystr tkey, tkey2;
  174.  
  175.   if (fno != filno) {
  176.     getSingleRec(fno);
  177. #ifdef _USESMEMOS_
  178.     clearMemoFields(fno);
  179. #endif
  180.     addarec(fno);
  181.     addThisRec.fileNumber = fno;
  182.     addThisRec.recordRef = recno[fno];
  183.     fwrite(&addThisRec,sizeof(fileAndRecRef),1,hFile);
  184.   }
  185.   for (i = 1; i <= maxfilno; i++) {
  186.     if ((linkback[i].t == _Unique) &&
  187.        (linkback[i].f == fno) && filinuse[i]) {
  188.       getlink(tkey,i);  strcpy(tkey2,tkey);
  189.       searchkey(idxkey[i][1],&recno[i],tkey2);
  190.       savOK = ok && (strsearch(tkey2,tkey) == tkey2);
  191.       while (savOK) {
  192.         duplicateBranch(i);
  193.         nextkey(idxkey[i][1],&recno[i],tkey2);
  194.         savOK = ok &&(strsearch(tkey2,tkey) == tkey2);
  195.       }
  196.       ok = True;  /* Restore OK to True */
  197.     }
  198.   }
  199. }
  200.  
  201. void setLinksAndAddKeys(void)
  202. {
  203.   int i, j, fno;
  204.   keystr tkey;
  205.   fileAndRecRef changeThisRec;
  206.  
  207.   fseek(hFile,0L,SEEK_SET);
  208.   while (!(feof(hFile))) {
  209.     if (fread(&changeThisRec,sizeof(fileAndRecRef),1,hFile) == 1) {
  210.       fno = changeThisRec.fileNumber;
  211.       recno[fno] = changeThisRec.recordRef;
  212.       getSingleRec(fno);  recavail[fno] = True;
  213.       recDefaultValues(fno);          /* Calls SetLinkage to set link fields */
  214.       putarec(fno);
  215. #ifdef _USESADJUST_
  216.       adjust(fno,'A');
  217. #endif
  218.       for (i=1;i<=maxkeyno; i++) {
  219.         if (keylen[fno][i] > 0) {
  220.           getakey(tkey,fno,i);
  221.           addkey(idxkey[fno][i],&recno[fno],tkey);
  222.         }
  223.       }
  224.     }
  225.   }
  226. }
  227.  
  228. void copyBranch(int fno, long copyRec)
  229. {
  230.   long i;
  231.   long parentRec;
  232.  
  233.   hFile = fopen("~COPYREC.HLD","rb");
  234.   if (hFile != NULL) {
  235.  
  236.     parentRec = recno[fno];
  237.     recno[fno] = copyRec;
  238.     getSingleRec(fno);
  239.     duplicateBranch(fno);
  240.  
  241.     recno[fno] = parentRec;
  242.     getSingleRec(fno);
  243.     setLinksAndAddKeys();
  244.  
  245.     fclose(hFile);
  246.     unlink("~COPYREC.HLD");
  247.  
  248.     scrn_active = False;
  249.     for (i=1; i<=maxfilno; i++) clear_rec(i);
  250.     scrn_active = True;
  251.     align_rec(recno[fno]);
  252.  
  253.   }
  254.   else
  255.     dberrm(msg_NeedHoldingFile);
  256. }
  257.  
  258. ----------------------------------------------------------------------------
  259.  
  260. 6)  Finally we need to add a little more code to the skeleton so that we can
  261.     make a call to this great new function "CopyBranch".
  262.  
  263.     What we will do is activate the function by pressing <Shift>+<F1>.  This
  264.     key combination needs to be picked up in the "do_menu()" function.  We
  265.     will then pass control to the "do_proc()" function where we will
  266.     implement the function call.
  267.  
  268.     In implementing the function we will first utilise a call to the normal
  269.     "copy" process to copy the record from the current file as we normally
  270.     would.  This ensures that any unique linking key will not be violated.
  271.     If the "copy" is successful then "copyBranch()" will be invoked to
  272.     transfer the rest of the branch.
  273.  
  274.     We also need to add an include directive for the function file that you
  275.     have just written, #include "copybran.fun", just before the "do_proc"
  276.     function so that the new functions will be available in the skeleton, :--
  277.  
  278.         #include "copybran.fun"                /* <-- New */
  279.  
  280.         void do_proc(int procno, mnufrec *m)
  281.         {
  282.           long copyRecno;                      /* <-- New */
  283.         ...
  284.         ...
  285.             case 99: if (status_ok(filno)) {
  286.               copyRecno = recno[filno];
  287.               add_record('C');
  288.               if (exitcode == XeptKey) copyBranch(filno,copyRecno);
  289.             } break;
  290.         ...
  291.         ...
  292.         }
  293.  
  294.         void do_menu(void)
  295.         {
  296.         ...
  297.         ...
  298.                     case LArr   : gorl(&dm,Left);  break;
  299.                     case RArr   : gorl(&dm,Right); break;
  300. /*New*/   case sF1    : do_proc(99,&dm); break;
  301.                     case Enter  : if (gvar->sec >= dm.curitm->sec) do_ctl(&dm,dm.curitm); break;
  302.         ...
  303.         ...
  304.         }
  305.