Topic
  • 17 replies
  • Latest Post - ‏2012-05-10T14:52:18Z by SystemAdmin
SystemAdmin
SystemAdmin
535 Posts

Pinned topic using linear-main procedures

‏2011-08-26T20:55:27Z |
I have a simple linear-main proc. with an F spec inside the proc. The file is apparently automatically opened and closed even though I am doing a return. Is that correct? or is it closed by the *new activation group going away? By eliminating then the RPG cycle, you have to manage the file opens and closes, right?
Updated on 2012-05-10T14:52:18Z at 2012-05-10T14:52:18Z by SystemAdmin
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2011-08-26T22:07:01Z  
    If you code the file inside the proc, it behaves the same as a local file in any procedure. If it doesn't have the STATIC keyword, it gets opened when the procedure starts and gets closed when the procedure ends. If you do have the STATIC keyword, it gets opened when the procedure is first called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    If you code the file in global F specs, before any P specs, then it behaves the same as a file in a NOMAIN module. It gets opened the first time any procedure in the module is called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    No matter how you code the file, as a global or local, as static or not, the file will be closed when the program ends if you're using the *NEW activation group. For experimenting to find out how files are opened/closed with a linear main, you'll probably learn more if you use a named activation group.
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2011-08-27T02:26:13Z  
    If you code the file inside the proc, it behaves the same as a local file in any procedure. If it doesn't have the STATIC keyword, it gets opened when the procedure starts and gets closed when the procedure ends. If you do have the STATIC keyword, it gets opened when the procedure is first called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    If you code the file in global F specs, before any P specs, then it behaves the same as a file in a NOMAIN module. It gets opened the first time any procedure in the module is called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    No matter how you code the file, as a global or local, as static or not, the file will be closed when the program ends if you're using the *NEW activation group. For experimenting to find out how files are opened/closed with a linear main, you'll probably learn more if you use a named activation group.
    Thanks for clearing that up for me Barbara. Your input is helpful as always.
  • Anvil
    Anvil
    2 Posts

    Re: using linear-main procedures

    ‏2012-03-28T17:41:20Z  
    If you code the file inside the proc, it behaves the same as a local file in any procedure. If it doesn't have the STATIC keyword, it gets opened when the procedure starts and gets closed when the procedure ends. If you do have the STATIC keyword, it gets opened when the procedure is first called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    If you code the file in global F specs, before any P specs, then it behaves the same as a file in a NOMAIN module. It gets opened the first time any procedure in the module is called, and it never gets closed unless you use a CLOSE opcode, or the activation group ends.

    No matter how you code the file, as a global or local, as static or not, the file will be closed when the program ends if you're using the *NEW activation group. For experimenting to find out how files are opened/closed with a linear main, you'll probably learn more if you use a named activation group.
    What is the "best practice" for defining files when using linear main procedures? Should they be defined in the "global" section (before P specs) or in the local section (after 1st P spec)? When I say "best practice", I mean in terms of performance, maintainability, file management...
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-03-29T15:17:30Z  
    • Anvil
    • ‏2012-03-28T17:41:20Z
    What is the "best practice" for defining files when using linear main procedures? Should they be defined in the "global" section (before P specs) or in the local section (after 1st P spec)? When I say "best practice", I mean in terms of performance, maintainability, file management...
    Anvil, a linear main module is very similar to a no-main module.

    This doesn't answer your question, but you may already have some ideas about your question in terms of a no-main module.

    Actually, I'm not sure there is a best practice anyway. It depends on how you want to use the file.

    One thing that's not really common to a no-main module is that when you want all the procedures in the module to use the file, you have two options:
    1. Code it as a global file.
    2. Code it local to the main procedure and pass it around the other procedures as a parameter. That way, you have more control over which procedures have access to the file. And if it's coded without the STATIC keyword, it will automatically get closed when the main procedure returns.

    Below is a working example of coding the file locally in the main procedure and passing it around to the subprocedures. Recoding this to compare how it would look using a global file is "left as an exercise for the keen student" (hehe).

    Try the same exercise for an externally-described file. A global file would mean that all the procedures have access to the file and to the fields in the file which would also be global. Using a local file and file parameters would mean that all the procedures in the module would have to use data structures for I/O. Again, depending on circumstances that might be good (more control over where the data goes) or bad (can feel clumsy and awkward compared to just using the fields directly).

    
    H main(fileParm)   FprtFile_T o    f   80        printer template D prtDs_T         ds            80    template   P fileParm        b FprintFile                            likefile(prtFile_T) F                                     extfile(
    'QPRINT') /free prtHeader(printFile); prtDetail(printFile : 
    'Hello'); prtDetail(printFile : 
    'world'); /end-free P fileParm        e   P prtHeader       b D                 pi D   prtFile                           likefile(prtFile_T) /free print (prtFile : 
    'Some greetings from program FILEPARM'); /end-free P prtHeader       e   P prtDetail       b D                 pi D   prtFile                           likefile(prtFile_T) D   line                        50a   varying 
    
    const /free print (prtFile : 
    '  - ' + line); /end-free P prtDetail       e   P print           b D                 pi D   prtFile                           likefile(prtFile_T) D   line                              likeDs(prtDs_T) 
    
    const /free write prtFile line; /end-free P print e
    
  • Anvil
    Anvil
    2 Posts

    Re: using linear-main procedures

    ‏2012-03-29T17:41:59Z  
    Anvil, a linear main module is very similar to a no-main module.

    This doesn't answer your question, but you may already have some ideas about your question in terms of a no-main module.

    Actually, I'm not sure there is a best practice anyway. It depends on how you want to use the file.

    One thing that's not really common to a no-main module is that when you want all the procedures in the module to use the file, you have two options:
    1. Code it as a global file.
    2. Code it local to the main procedure and pass it around the other procedures as a parameter. That way, you have more control over which procedures have access to the file. And if it's coded without the STATIC keyword, it will automatically get closed when the main procedure returns.

    Below is a working example of coding the file locally in the main procedure and passing it around to the subprocedures. Recoding this to compare how it would look using a global file is "left as an exercise for the keen student" (hehe).

    Try the same exercise for an externally-described file. A global file would mean that all the procedures have access to the file and to the fields in the file which would also be global. Using a local file and file parameters would mean that all the procedures in the module would have to use data structures for I/O. Again, depending on circumstances that might be good (more control over where the data goes) or bad (can feel clumsy and awkward compared to just using the fields directly).

    <pre class="jive-pre"> H main(fileParm) FprtFile_T o f 80 printer template D prtDs_T ds 80 template P fileParm b FprintFile likefile(prtFile_T) F extfile( 'QPRINT') /free prtHeader(printFile); prtDetail(printFile : 'Hello'); prtDetail(printFile : 'world'); /end-free P fileParm e P prtHeader b D pi D prtFile likefile(prtFile_T) /free print (prtFile : 'Some greetings from program FILEPARM'); /end-free P prtHeader e P prtDetail b D pi D prtFile likefile(prtFile_T) D line 50a varying const /free print (prtFile : ' - ' + line); /end-free P prtDetail e P print b D pi D prtFile likefile(prtFile_T) D line likeDs(prtDs_T) const /free write prtFile line; /end-free P print e </pre>
    In the vast majority of cases I have only 1 procedure in my linear main modules, so, in that case is there a "best practice"? I know there is more coding when it is local because of having to create data structures for input and output of records, but does that inconvenience offset any possible performance gains compared to making the files global? I can see how having multiple procedures in a module might warrant the use of local files based on your philosophy of managing local/global variables in that case...
  • scott_klement
    scott_klement
    245 Posts

    Re: using linear-main procedures

    ‏2012-04-02T17:41:40Z  
    • Anvil
    • ‏2012-03-29T17:41:59Z
    In the vast majority of cases I have only 1 procedure in my linear main modules, so, in that case is there a "best practice"? I know there is more coding when it is local because of having to create data structures for input and output of records, but does that inconvenience offset any possible performance gains compared to making the files global? I can see how having multiple procedures in a module might warrant the use of local files based on your philosophy of managing local/global variables in that case...
    For a single procedure there's no performance difference between a local or global file. Just do whatever makes your code easier to read.
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-04-03T15:55:28Z  
    For a single procedure there's no performance difference between a local or global file. Just do whatever makes your code easier to read.
    Scott is right that there is no difference in performance. At least, there's no difference in the performance for the I/O operations.

    But there could be a difference in the overall performance of your program if you are not careful.

    If you code an ordinary global F spec in a linear main module with no other procedures, the file will be implicitly opened when the main procedure is first called, and it will never be closed until it does a CLOSE opcode. (Similar to returning with LR off in a cycle module.)

    If you code the exact same F spec local to a subprocedure, the file will be implicitly opened and closed every time the procedure gets called. (Similar to always setting on LR in a cycle module.)

    If you would always be closing the file at the end of your main procedure and opening it on the next call, then the performance of the local file is the same. But if you want the file to remain open across calls, you have to do a tiny bit more coding to avoid the repeated opens and closes and get the same overall performance as if the file were global.

    If you want your file to remain open when your main procedure ends so that it is still open when the main procedure is called again, then for a local file you need to add the STATIC keyword to the file. That way, the file will be opened the first time the main procedure gets called, and it will only be closed when you use the CLOSE opcode, and the performance will be the same as for a global file.
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-04-26T19:51:23Z  
    Scott is right that there is no difference in performance. At least, there's no difference in the performance for the I/O operations.

    But there could be a difference in the overall performance of your program if you are not careful.

    If you code an ordinary global F spec in a linear main module with no other procedures, the file will be implicitly opened when the main procedure is first called, and it will never be closed until it does a CLOSE opcode. (Similar to returning with LR off in a cycle module.)

    If you code the exact same F spec local to a subprocedure, the file will be implicitly opened and closed every time the procedure gets called. (Similar to always setting on LR in a cycle module.)

    If you would always be closing the file at the end of your main procedure and opening it on the next call, then the performance of the local file is the same. But if you want the file to remain open across calls, you have to do a tiny bit more coding to avoid the repeated opens and closes and get the same overall performance as if the file were global.

    If you want your file to remain open when your main procedure ends so that it is still open when the main procedure is called again, then for a local file you need to add the STATIC keyword to the file. That way, the file will be opened the first time the main procedure gets called, and it will only be closed when you use the CLOSE opcode, and the performance will be the same as for a global file.
    Thanks Barbara and Scott.

    Here is another issue I am unsure about as I begin converting some programs to linear-main. Here is a snippet:

    
    *###### header specifications ######* h option(*nodebugio) h text(*srcmbrtxt) h copyright(
    'My Company, Inc.') h*actgrp(*caller) h*dftactgrp(*no) h ccsid(*
    
    char : *jobrun) h main(ita281b)     *###### global specifications ######* * application constants /copy qcpylesrc,uua1000r   * prototypes *** edit excel template /copy qcpylesrc,ita281b     *###### local specifications ######* p ita281b         b
    


    This SQLRPGLE program (embedded SQL) will only compile when I comment out the activation group related H specs (It will also not let me embed copy sources within each other). Can I not use the 2 commented out H specs with a linear-main, SQLRPG?
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-04-26T20:58:52Z  
    Thanks Barbara and Scott.

    Here is another issue I am unsure about as I begin converting some programs to linear-main. Here is a snippet:

    <pre class="jive-pre"> *###### header specifications ######* h option(*nodebugio) h text(*srcmbrtxt) h copyright( 'My Company, Inc.') h*actgrp(*caller) h*dftactgrp(*no) h ccsid(* char : *jobrun) h main(ita281b) *###### global specifications ######* * application constants /copy qcpylesrc,uua1000r * prototypes *** edit excel template /copy qcpylesrc,ita281b *###### local specifications ######* p ita281b b </pre>

    This SQLRPGLE program (embedded SQL) will only compile when I comment out the activation group related H specs (It will also not let me embed copy sources within each other). Can I not use the 2 commented out H specs with a linear-main, SQLRPG?
    About those two keywords, you can only use them if you specify OBJTYPE(*PGM) for CRTSQLRPGI. I think it defaults to *PGM, but maybe you have a different default.

    If you're trying to create a module, then you can't have those keywords. They are only allowed with CRTSQLRPGI OBJTYPE(*PGM) or CRTBNDRPG.

    What you could do is code those keywords like this so that they only get used when you are creating a program.
    
    /
    
    if defined(*crtbndrpg) H dftactgrp(*no) H actgrp(*caller) /endif
    


    About the nested /COPY, the SQL precompiler doesn't support that. You have a couple of options

    • you can compile with CRTSQLRPGI RPGPPOPT(*LVL1) or *LVL2. That will allow the nested /COPY.
    • you can change one or both of the /COPY directives to /INCLUDE. The two mean exactly the same thing to the RPG compiler, but the SQL precompiler ignores /INCLUDE.
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-04-27T13:06:13Z  
    About those two keywords, you can only use them if you specify OBJTYPE(*PGM) for CRTSQLRPGI. I think it defaults to *PGM, but maybe you have a different default.

    If you're trying to create a module, then you can't have those keywords. They are only allowed with CRTSQLRPGI OBJTYPE(*PGM) or CRTBNDRPG.

    What you could do is code those keywords like this so that they only get used when you are creating a program.
    <pre class="jive-pre"> / if defined(*crtbndrpg) H dftactgrp(*no) H actgrp(*caller) /endif </pre>

    About the nested /COPY, the SQL precompiler doesn't support that. You have a couple of options

    • you can compile with CRTSQLRPGI RPGPPOPT(*LVL1) or *LVL2. That will allow the nested /COPY.
    • you can change one or both of the /COPY directives to /INCLUDE. The two mean exactly the same thing to the RPG compiler, but the SQL precompiler ignores /INCLUDE.
    Got it to work! Thanks.
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-04-27T13:13:02Z  
    About those two keywords, you can only use them if you specify OBJTYPE(*PGM) for CRTSQLRPGI. I think it defaults to *PGM, but maybe you have a different default.

    If you're trying to create a module, then you can't have those keywords. They are only allowed with CRTSQLRPGI OBJTYPE(*PGM) or CRTBNDRPG.

    What you could do is code those keywords like this so that they only get used when you are creating a program.
    <pre class="jive-pre"> / if defined(*crtbndrpg) H dftactgrp(*no) H actgrp(*caller) /endif </pre>

    About the nested /COPY, the SQL precompiler doesn't support that. You have a couple of options

    • you can compile with CRTSQLRPGI RPGPPOPT(*LVL1) or *LVL2. That will allow the nested /COPY.
    • you can change one or both of the /COPY directives to /INCLUDE. The two mean exactly the same thing to the RPG compiler, but the SQL precompiler ignores /INCLUDE.
    As always, thanks for your help.

    I seems that WORKSTN files don't mix as easily with the linear-main module as DISK files do, especially when using the INFDS and INDDS structures. I had to end up defining them globally instead of locally. Am I missing something, or are they a little quirky when defined locally?
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-04-27T16:28:12Z  
    As always, thanks for your help.

    I seems that WORKSTN files don't mix as easily with the linear-main module as DISK files do, especially when using the INFDS and INDDS structures. I had to end up defining them globally instead of locally. Am I missing something, or are they a little quirky when defined locally?
    What do you mean by quirky? Did you get a compile-time error or a run-time error or strange results or ... ?

    For what it's worth, this works for me:
    
    H main(mypgm) D mypgm           pr                  extpgm(
    'MYPGM')   P mypgm           b Fmydspf    cf   e             workstn infds(infds) F                                     indds(indds) D infds           ds           100 D indds           ds            99 
    // ... P mypgm           e
    
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-05-09T16:47:27Z  
    What do you mean by quirky? Did you get a compile-time error or a run-time error or strange results or ... ?

    For what it's worth, this works for me:
    <pre class="jive-pre"> H main(mypgm) D mypgm pr extpgm( 'MYPGM') P mypgm b Fmydspf cf e workstn infds(infds) F indds(indds) D infds ds 100 D indds ds 99 // ... P mypgm e </pre>
    Well one thing that is different in my pgm is that I am defining INFDS and INDDS in the global section (it is included as part of an application constants copysrc). Would that matter?
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-05-09T17:32:30Z  
    Well one thing that is different in my pgm is that I am defining INFDS and INDDS in the global section (it is included as part of an application constants copysrc). Would that matter?
    Maybe it would matter. It sounds like you are seeing a difference. In your earlier post you said you ended up having to define the INFDS and INDDS globally.

    What problem were you having when you defined them locally that got fixed when you defined them globally?
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-05-09T17:45:49Z  
    Maybe it would matter. It sounds like you are seeing a difference. In your earlier post you said you ended up having to define the INFDS and INDDS globally.

    What problem were you having when you defined them locally that got fixed when you defined them globally?
    It does not compile. I get: File ITA290AD and INFDS KEYBOARD not defined with the same owning procedure and storage type

    Here is the code:

    
    * standard header specs /copy qcpylesrc,uua1001r h main(ita290a)     *###### global section ######* * workstation configuration *** file definition   * application constants /copy qcpylesrc,uua1000r   * prototypes *** label prompt /copy qcpylesrc,ita290a     *###### local section ######* p ita290a         b   * database configuration *** file definitions FIT0200A   IF   E           K DISK    RENAME(IT20Rcd:LablFilter) F                                     
    
    static FITA290AD  CF   E             WORKSTN INFDS(Keyboard) F                                     INDDS(Indicators) F SFILE(lblpsfd:lbSC.#LPSRN) f                                     usropn *** file buffers d lbWF            ds                  likerec(lblpwfr:*output) d lbSD            ds                  likerec(lblpsfd:*all) inz d lbSC            ds                  likerec(lblpsfc:*all) inz d lbTl            ds                  likerec(lblpttl:*output) d lbOp            ds                  likerec(lblpopt:*output) d lbNR            ds                  likerec(lblpnor:*output) d lbCm            ds                  likerec(lblpcmd:*output) *** file buffers d lbIn            ds                  likerec(LablFilter:*input)   * procedure 
    
    interface d ita290a         pi d  Label                         2a
    


    the defs in UUA1000R are as follows:

    
    * program status data structure d                sds           263 d UserID                254    263a   * display file indicator representation d Indicators      ds d  dView                 81     81n   inz(*off)                            View Option d  dEdit                 82     82n   inz(*off)                            Edit Option d  dCopy                 83     83n   inz(*off)                            Copy Option d  dDelete               84     84n   inz(*off)                            Delete Option d  dAdd                  86     86n   inz(*off)                            Add Option d  dClrGrid              87     87n   inz(*off)                            Clear Grid d  dHidGrid              88     88n   inz(*off)                            Hide Grid d  dEndGrid              89     89n   inz(*off)                            End of Grid   * representation of which key was pressed on the keyboard d Keyboard        ds d  KeyPressed           369    369 Key Pressed
    
  • barbara_morris
    barbara_morris
    393 Posts

    Re: using linear-main procedures

    ‏2012-05-09T21:51:01Z  
    It does not compile. I get: File ITA290AD and INFDS KEYBOARD not defined with the same owning procedure and storage type

    Here is the code:

    <pre class="jive-pre"> * standard header specs /copy qcpylesrc,uua1001r h main(ita290a) *###### global section ######* * workstation configuration *** file definition * application constants /copy qcpylesrc,uua1000r * prototypes *** label prompt /copy qcpylesrc,ita290a *###### local section ######* p ita290a b * database configuration *** file definitions FIT0200A IF E K DISK RENAME(IT20Rcd:LablFilter) F static FITA290AD CF E WORKSTN INFDS(Keyboard) F INDDS(Indicators) F SFILE(lblpsfd:lbSC.#LPSRN) f usropn *** file buffers d lbWF ds likerec(lblpwfr:*output) d lbSD ds likerec(lblpsfd:*all) inz d lbSC ds likerec(lblpsfc:*all) inz d lbTl ds likerec(lblpttl:*output) d lbOp ds likerec(lblpopt:*output) d lbNR ds likerec(lblpnor:*output) d lbCm ds likerec(lblpcmd:*output) *** file buffers d lbIn ds likerec(LablFilter:*input) * procedure interface d ita290a pi d Label 2a </pre>

    the defs in UUA1000R are as follows:

    <pre class="jive-pre"> * program status data structure d sds 263 d UserID 254 263a * display file indicator representation d Indicators ds d dView 81 81n inz(*off) View Option d dEdit 82 82n inz(*off) Edit Option d dCopy 83 83n inz(*off) Copy Option d dDelete 84 84n inz(*off) Delete Option d dAdd 86 86n inz(*off) Add Option d dClrGrid 87 87n inz(*off) Clear Grid d dHidGrid 88 88n inz(*off) Hide Grid d dEndGrid 89 89n inz(*off) End of Grid * representation of which key was pressed on the keyboard d Keyboard ds d KeyPressed 369 369 Key Pressed </pre>
    Ah, ok. I guess that does qualify as quirky.

    I think this only matters for the INFDS. There's no rule like that for INDDS. The reason for the INFDS rule is that the INFDS has to have the same "lifetime" as the file. They both have to be initialized at the same time, which means they have to be in the same procedure, and have to have the same storage type.

    So if you want to have a local file with an INFDS, you have to also define the INFDS locally, and they both have to have the STATIC keyword, or both not have the STATIC keyword.

    For your INFDS in the procedure, you could define the INFDS using LIKEDS of the data structure that is brought in through the /COPY file. But then you'd have to refer to the subfield using the qualified name.
    
    FITA290AD  CF   E             WORKSTN INFDS(LKeyboard) D LKeyboard       ds                  likeds(Keyboard)   ... 
    
    if LKeyboard.KeyPressed = something;
    

    It looks like you're already using qualified structures anyway, so that might not be a problem.

    If you also have /copy files with calculations using that subfield, then you could create another copy file that just has that data structure, that you can /copy within subprocedure. Or you could create additional versions of the calculation copy files that use the LKeyboard.KeyPressed qualified name.

    Maybe that's too much jumping through hoops just to avoid having a global file. There's nothing necessarily wrong with a global file anyway.
  • SystemAdmin
    SystemAdmin
    535 Posts

    Re: using linear-main procedures

    ‏2012-05-10T14:52:18Z  
    Ah, ok. I guess that does qualify as quirky.

    I think this only matters for the INFDS. There's no rule like that for INDDS. The reason for the INFDS rule is that the INFDS has to have the same "lifetime" as the file. They both have to be initialized at the same time, which means they have to be in the same procedure, and have to have the same storage type.

    So if you want to have a local file with an INFDS, you have to also define the INFDS locally, and they both have to have the STATIC keyword, or both not have the STATIC keyword.

    For your INFDS in the procedure, you could define the INFDS using LIKEDS of the data structure that is brought in through the /COPY file. But then you'd have to refer to the subfield using the qualified name.
    <pre class="jive-pre"> FITA290AD CF E WORKSTN INFDS(LKeyboard) D LKeyboard ds likeds(Keyboard) ... if LKeyboard.KeyPressed = something; </pre>
    It looks like you're already using qualified structures anyway, so that might not be a problem.

    If you also have /copy files with calculations using that subfield, then you could create another copy file that just has that data structure, that you can /copy within subprocedure. Or you could create additional versions of the calculation copy files that use the LKeyboard.KeyPressed qualified name.

    Maybe that's too much jumping through hoops just to avoid having a global file. There's nothing necessarily wrong with a global file anyway.
    I like having all files in the same "spot" in my programs. I think it makes things less confusing for anyone coming behind me. LIKEDS is a good suggestion, I will have to think about what I want to do to keep them together. Thanks for your help!