Есть две возможности ...
Для тех, кто не знаком с внутренней структурой секционированных наборов данных (то есть PDS или PDS / E), эти наборы данных логически разделены на две части: «каталог» "имея указатели на все отдельные элементы, и область" данных ", содержащую фактические записи для отдельных элементов:
PDS: <DIRECTORY BLOCKS>
<MEMBER1>: ADDRESS OF DATA FOR MEMBER1 (xxx)
<MEMBER2>: ADDRESS OF DATA FOR MEMBER2 (yyy)
...
<DIRECTORY FREESPACE)
...
<EOF - END OF THE PDS DIRECTORY>
<DATA PORTION>
+xxx = DATA FOR MEMBER1
...
<EOF - END OF MEMBER1>
+yyy = DATA FOR MEMBER2
...
<EOF - END OF MEMBER2>
...
FREE SPACE (ALLOCATED, BUT UNUSED)
...
END OF PDS
В следующих нескольких абзацах имейте в виду, что вы можете открыть любой PDS целиком / PDSE, и это позволяет вам читать / записывать любые элементы, которые вам нравятся, или вы можете выделить и открыть один член, и он будет обрабатываться, как любой другой последовательный файл.
Во-первых, если у вас действительно есть оператор DD, закодированный так, как вы показываете в вопросе, тогда вам может просто потребоваться изменить ваше открытие с fopen(dsn,...)
на fopen(dd:ddname,...)
. Если вы работаете в оболочке UNIX или делаете что-то, что приводит к тому, что ваш процесс работает в другом адресном пространстве (например, fork ()), то это может не сработать, но, возможно, стоит попробовать. Если вы сделаете это с помощью JCL, который вы показываете, проблема будет в управлении каталогом PDS / E - вам нужно будет создать свой собственный "STOW" при создании / обновлении нового члена, поскольку JCL выделяет весь набор данных, а не только одинокий член. Последовательность будет следующей:
- Откройте DD для вывода.
- Запишите свои данные.
- Обновите каталог PDS или PDS / E информацией о новом члене (это здесь появляется функция STOW - она обновляет каталог PDS / PDSE, чтобы отразить член, который вы создали или обновили).
- Закройте файл
Если вам также нужно read members, вам нужно будет выполнить FIND (или BLDL / POINT - который может быть fseek () в C), чтобы указать на правильный член, а затем прочитать член. Я уверен, что это звучит сложно, но преимущество этого подхода в том, что вы можете выделить / открыть файл один раз и обработать столько отдельных членов, сколько захотите.
Второй обходной путь может заключаться в том, чтобы динамически выделить файл самостоятельно, а затем открыть его, используя синтаксис DD:ddname
... если вы нечасто обращаетесь к файлу, это, вероятно, проще закодировать. Кровавые детали распределения динамических c полностью описаны здесь: https://www.ibm.com/support/knowledgecenter/SSLTBW_2.4.0/com.ibm.zos.v2r4.ieaa800/reqsvc.htm.
Есть несколько способов вызвать динамическое c распределение: вы можете написать небольшую программу на ассемблере, можно использовать вызываемую службу BPXWDYN z / OS UNIX Services или функции среды выполнения C "dynallo c ()" или "svc99 ()". Функция dynallo c () проста в использовании, но она раскрывает только часть того, что может сделать динамическое c распределение ... svc99 () более громоздко в использовании, но предоставляет больше функциональных возможностей.
Как бы вы это ни делали, динамическое c распределение требует «текстовых единиц», которые примерно соответствуют параметрам, которые вы найдете в операторах JCL DD. То, что вы описываете, звучит так, будто вам просто нужно передать текстовые блоки DSN и DISP и, возможно, DDNAME (вы можете либо передать свое собственное DDNAME, либо позволить системе сгенерировать его для вас).
Функции времени выполнения C упрощают все это, но имейте в виду, что есть несколько странностей, таких как необходимость дополнить параметры до максимальной длины. Например, DSN должен состоять из 44 символов и дополнен пробелами справа, а не строкой с завершающим нулем в стиле C.
Вот небольшой фрагмент кода в качестве примера:
#include <dynit.h>
. . .
int allocate(ddn, dsn, mem)
{
__dyn_t ip; // Parameters to dynalloc()
. . .
// Prepare the parameters to dynalloc()
dyninit(&ip); // Initialize the parameters
ip.__ddname = ddn; // 8-char blank-padded
ip.__dsname = dsn; // 44-char blank-padded
ip.__status = __DISP_SHR; // DISP=(SHR)
ip.__normdisp = __DISP_KEEP; // DISP=(...,KEEP)
ip.__misc_flags = __CLOSE; // FREE=CLOSE
if (*mem) // Optional PDS, PDS/E member
ip.__member = mem; // 8-char blank-padded
// Now we can call dynalloc()...
if (dynalloc(&ip)) // 0: Success, else error
{
// On error, the errcode/infocode explain why - values
// are detailed in z/OS Authorized Services Reference
printf("SVC99: Can't allocate %s - RC 0x%x, Info 0x%x\n",
dsn, ip.__errcode, ip.__infocode);
return FALSE;
}
// If dynalloc works, you can open the file with fopen("DD:ddname",...)
}
Не забывайте, что когда вы закончите работу с файлом, вам обычно нужно освободить его. В приведенном выше фрагменте кода используется «FREE = CLOSE» - это означает, что при закрытии файла z / OS автоматически освобождает выделение… если вы открываете и обрабатываете набор данных только один раз, это удобный подход. Если вам нужно неоднократно открывать и закрывать файл, вы не будете использовать FREE = CLOSE, а вместо этого вызовите динамическое c распределение во второй раз после того, как вы закончите свою обработку и захотите освободить файл.
Если вам нужен одновременный доступ к нескольким файлам, имейте в виду, что вам нужно будет сгенерировать несколько уникальных DDNAME. Вы можете сделать это в своем собственном коде или использовать форму динамического c выделения, которая автоматически создает и возвращает пригодное для использования DDNAME (в форме «SYSnnnnn»).
Также не забывайте, что обновление набора данных с помощью DISP = SHR может быть опасным в некоторых ситуациях, особенно если задействованный набор данных может быть как обычным PDS, так и PDS / E. Большая опасность заключается в том, что два приложения одновременно открывают набор данных для вывода ... оба будут записывать данные в одно и то же место, и результатом, скорее всего, будет поврежденный каталог PDS.
В среде UNIX Services есть и другие странности, особенно если вы используете fork () или exe c () и ожидаете, что дескрипторы файлов будут работать в подпроцессах, поскольку выделения обычно привязаны к конкретному z Адресное пространство / OS. Такие службы, как spawn (), могут позволить дочернему процессу работать в том же адресном пространстве, поэтому это одна из возможностей.