mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Major update of ecpg preprocessor
From: Michael Meskes <meskes@topsystem.de>
This commit is contained in:
@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# IDENTIFICATION
|
||||||
# $Header: /cvsroot/pgsql/src/interfaces/Makefile,v 1.6 1998/02/12 02:14:14 scrappy Exp $
|
# $Header: /cvsroot/pgsql/src/interfaces/Makefile,v 1.7 1998/02/17 01:47:19 scrappy Exp $
|
||||||
#
|
#
|
||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ include $(SRCDIR)/Makefile.global
|
|||||||
|
|
||||||
.DEFAULT all:
|
.DEFAULT all:
|
||||||
$(MAKE) -C libpq $@
|
$(MAKE) -C libpq $@
|
||||||
# $(MAKE) -C ecpg $@
|
$(MAKE) -C ecpg $@
|
||||||
ifeq ($(HAVE_Cplusplus), true)
|
ifeq ($(HAVE_Cplusplus), true)
|
||||||
$(MAKE) -C libpq++ $@
|
$(MAKE) -C libpq++ $@
|
||||||
else
|
else
|
||||||
|
@ -9,3 +9,17 @@ Wed Feb 11 10:58:13 CET 1998
|
|||||||
Thu Feb 12 14:45:07 CET 1998
|
Thu Feb 12 14:45:07 CET 1998
|
||||||
|
|
||||||
- Changed parser to correctly handle local variables.
|
- Changed parser to correctly handle local variables.
|
||||||
|
- Allow static and extern variable definitions.
|
||||||
|
- free() variable structure completely.
|
||||||
|
|
||||||
|
Fri Feb 13 12:35:58 CET 1998
|
||||||
|
|
||||||
|
- ecpg can use structs to store data, but only if the complete
|
||||||
|
definition of the struct lies inside the sql declare section
|
||||||
|
and only simple types used.
|
||||||
|
|
||||||
|
Fre Feb 13 14:12:41 CET 1998
|
||||||
|
|
||||||
|
- Structure now work completely.
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,10 +16,6 @@ just -1 for them all.
|
|||||||
|
|
||||||
Missing library functions to_date et al.
|
Missing library functions to_date et al.
|
||||||
|
|
||||||
Possibility to define records or structs in the declare section in a way
|
|
||||||
that the record can be filled from one row in the database. This is a
|
|
||||||
simpler way to handle an entire row at a time.
|
|
||||||
|
|
||||||
Oracle has array operations that enhances speed. When implementing it in
|
Oracle has array operations that enhances speed. When implementing it in
|
||||||
ecpg it is done for compatibility reasons only. For them to improve speed
|
ecpg it is done for compatibility reasons only. For them to improve speed
|
||||||
would require a lot more insight in the postgres internal mechanisms than I
|
would require a lot more insight in the postgres internal mechanisms than I
|
||||||
@ -44,5 +40,6 @@ could be realised in a script.
|
|||||||
|
|
||||||
Now comes my list (MM):
|
Now comes my list (MM):
|
||||||
|
|
||||||
Variable definitions containing static/volatile have to be possible.
|
What do we do with enum data types?
|
||||||
|
|
||||||
|
'signed' isn't understood so far
|
||||||
|
@ -47,7 +47,7 @@ $(shlib): ecpglib.o typename.o
|
|||||||
clean:
|
clean:
|
||||||
rm -f *.o *.a core a.out *~ $(shlib) libecpg.so
|
rm -f *.o *.a core a.out *~ $(shlib) libecpg.so
|
||||||
|
|
||||||
install: libecpg.a
|
install: libecpg.a $(shlib)
|
||||||
install -m 644 libecpg.a $(DESTDIR)$(LIBDIR)
|
install -m 644 libecpg.a $(DESTDIR)$(LIBDIR)
|
||||||
install -m 644 $(shlib) $(DESTDIR)$(LIBDIR)
|
install -m 644 $(shlib) $(DESTDIR)$(LIBDIR)
|
||||||
ln -sf $(shlib) $(DESTDIR)$(LIBDIR)/libecpg.so
|
ln -sf $(shlib) $(DESTDIR)$(LIBDIR)/libecpg.so
|
||||||
|
@ -51,12 +51,7 @@ main(int argc, char *const argv[])
|
|||||||
{
|
{
|
||||||
char *filename, *ptr2ext;
|
char *filename, *ptr2ext;
|
||||||
|
|
||||||
filename = malloc(strlen(argv[fnr]) + 2);
|
filename = mm_alloc(strlen(argv[fnr]) + 2);
|
||||||
if (filename == NULL)
|
|
||||||
{
|
|
||||||
perror("malloc");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(filename, argv[fnr]);
|
strcpy(filename, argv[fnr]);
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ extern FILE *yyin, *yyout;
|
|||||||
extern void lex_init(void);
|
extern void lex_init(void);
|
||||||
extern char * input_filename;
|
extern char * input_filename;
|
||||||
extern int yyparse(void);
|
extern int yyparse(void);
|
||||||
|
extern void *mm_alloc(size_t);
|
||||||
|
@ -58,6 +58,14 @@ float { dbg(S_FLOAT); return S_FLOAT; }
|
|||||||
double { dbg(S_DOUBLE); return S_DOUBLE; }
|
double { dbg(S_DOUBLE); return S_DOUBLE; }
|
||||||
bool { dbg(S_BOOL); return S_BOOL; }
|
bool { dbg(S_BOOL); return S_BOOL; }
|
||||||
|
|
||||||
|
static { dbg(S_STATIC); return S_STATIC; }
|
||||||
|
extern { dbg(S_EXTERN); return S_EXTERN; }
|
||||||
|
auto { dbg(S_AUTO); return S_AUTO; }
|
||||||
|
const { dbg(S_CONST); return S_CONST; }
|
||||||
|
register { dbg(S_REGISTER); return S_REGISTER; }
|
||||||
|
|
||||||
|
struct { dbg(S_STRUCT); return S_STRUCT; }
|
||||||
|
|
||||||
{string} { dbg(SQL_STRING); return SQL_STRING; }
|
{string} { dbg(SQL_STRING); return SQL_STRING; }
|
||||||
<SQL>{ws} ;
|
<SQL>{ws} ;
|
||||||
{symbol} { dbg(S_SYMBOL); return S_SYMBOL; }
|
{symbol} { dbg(S_SYMBOL); return S_SYMBOL; }
|
||||||
|
@ -13,6 +13,10 @@ static void yyerror(char *);
|
|||||||
* Variables containing simple states.
|
* Variables containing simple states.
|
||||||
*/
|
*/
|
||||||
int debugging = 0;
|
int debugging = 0;
|
||||||
|
static int struct_level = 0;
|
||||||
|
|
||||||
|
/* temporarily store record members while creating the data structure */
|
||||||
|
struct ECPGrecord_member *record_member_list[128] = { NULL };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle the filename and line numbering.
|
* Handle the filename and line numbering.
|
||||||
@ -86,7 +90,7 @@ remove_variables(int brace_level)
|
|||||||
{
|
{
|
||||||
struct variable * p, *prev;
|
struct variable * p, *prev;
|
||||||
|
|
||||||
for (p = prev = allvariables; p; p = p->next)
|
for (p = prev = allvariables; p; p = p ? p->next : NULL)
|
||||||
{
|
{
|
||||||
if (p->brace_level >= brace_level)
|
if (p->brace_level >= brace_level)
|
||||||
{
|
{
|
||||||
@ -96,6 +100,8 @@ remove_variables(int brace_level)
|
|||||||
else
|
else
|
||||||
prev->next = p->next;
|
prev->next = p->next;
|
||||||
|
|
||||||
|
ECPGfree_type(p->type);
|
||||||
|
free(p->name);
|
||||||
free(p);
|
free(p);
|
||||||
p = prev;
|
p = prev;
|
||||||
}
|
}
|
||||||
@ -157,7 +163,7 @@ dump_variables(struct arguments * list)
|
|||||||
dump_variables(list->next);
|
dump_variables(list->next);
|
||||||
|
|
||||||
/* Then the current element. */
|
/* Then the current element. */
|
||||||
ECPGdump_a_type(yyout, list->variable->name, list->variable->type);
|
ECPGdump_a_type(yyout, list->variable->name, list->variable->type, NULL);
|
||||||
|
|
||||||
/* Then release the list element. */
|
/* Then release the list element. */
|
||||||
free(list);
|
free(list);
|
||||||
@ -179,12 +185,12 @@ dump_variables(struct arguments * list)
|
|||||||
|
|
||||||
%token <tagname> S_SYMBOL S_LENGTH S_ANYTHING
|
%token <tagname> S_SYMBOL S_LENGTH S_ANYTHING
|
||||||
%token <tagname> S_VARCHAR S_VARCHAR2
|
%token <tagname> S_VARCHAR S_VARCHAR2
|
||||||
%token <tagname> S_EXTERN S_STATIC
|
%token <tagname> S_EXTERN S_STATIC S_AUTO S_CONST S_REGISTER S_STRUCT
|
||||||
%token <tagname> S_UNSIGNED S_SIGNED
|
%token <tagname> S_UNSIGNED S_SIGNED
|
||||||
%token <tagname> S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE S_BOOL
|
%token <tagname> S_LONG S_SHORT S_INT S_CHAR S_FLOAT S_DOUBLE S_BOOL
|
||||||
%token <tagname> '[' ']' ';' ',' '{' '}'
|
%token <tagname> '[' ']' ';' ',' '{' '}'
|
||||||
|
|
||||||
%type <type> type type_detailed varchar_type simple_type array_type
|
%type <type> type type_detailed varchar_type simple_type array_type struct_type
|
||||||
%type <symbolname> symbol
|
%type <symbolname> symbol
|
||||||
%type <tagname> maybe_storage_clause varchar_tag
|
%type <tagname> maybe_storage_clause varchar_tag
|
||||||
%type <type_enum> simple_tag
|
%type <type_enum> simple_tag
|
||||||
@ -227,8 +233,12 @@ variable_declarations : /* empty */
|
|||||||
|
|
||||||
/* Here is where we can enter support for typedef. */
|
/* Here is where we can enter support for typedef. */
|
||||||
variable_declaration : type ';' {
|
variable_declaration : type ';' {
|
||||||
|
/* don't worry about our list when we're working on a struct */
|
||||||
|
if (struct_level == 0)
|
||||||
|
{
|
||||||
new_variable($<type>1.name, $<type>1.typ);
|
new_variable($<type>1.name, $<type>1.typ);
|
||||||
free($<type>1.name);
|
free($<type>1.name);
|
||||||
|
}
|
||||||
fprintf(yyout, ";");
|
fprintf(yyout, ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,12 +254,18 @@ symbol : S_SYMBOL {
|
|||||||
type : maybe_storage_clause type_detailed { $<type>$ = $<type>2; };
|
type : maybe_storage_clause type_detailed { $<type>$ = $<type>2; };
|
||||||
type_detailed : varchar_type { $<type>$ = $<type>1; }
|
type_detailed : varchar_type { $<type>$ = $<type>1; }
|
||||||
| simple_type { $<type>$ = $<type>1; }
|
| simple_type { $<type>$ = $<type>1; }
|
||||||
| array_type {$<type>$ = $<type>1; };
|
| array_type {$<type>$ = $<type>1; }
|
||||||
|
| struct_type {$<type>$ = $<type>1; };
|
||||||
|
|
||||||
varchar_type : varchar_tag symbol index {
|
varchar_type : varchar_tag symbol index {
|
||||||
fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $<symbolname>2, $<indexsize>3, $<symbolname>2);
|
fprintf(yyout, "struct varchar_%s { int len; char arr[%d]; } %s", $<symbolname>2, $<indexsize>3, $<symbolname>2);
|
||||||
|
if (struct_level == 0)
|
||||||
|
{
|
||||||
$<type>$.name = $<symbolname>2;
|
$<type>$.name = $<symbolname>2;
|
||||||
$<type>$.typ = ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3);
|
$<type>$.typ = ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ECPGmake_record_member($<symbolname>2, ECPGmake_varchar_type(ECPGt_varchar, $<indexsize>3), &(record_member_list[struct_level-1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
varchar_tag : S_VARCHAR { $<tagname>$ = $<tagname>1; }
|
varchar_tag : S_VARCHAR { $<tagname>$ = $<tagname>1; }
|
||||||
@ -257,14 +273,42 @@ varchar_tag : S_VARCHAR { $<tagname>$ = $<tagname>1; }
|
|||||||
|
|
||||||
simple_type : simple_tag symbol {
|
simple_type : simple_tag symbol {
|
||||||
fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $<symbolname>2);
|
fprintf(yyout, "%s %s", ECPGtype_name($<type_enum>1), $<symbolname>2);
|
||||||
|
if (struct_level == 0)
|
||||||
|
{
|
||||||
$<type>$.name = $<symbolname>2;
|
$<type>$.name = $<symbolname>2;
|
||||||
$<type>$.typ = ECPGmake_simple_type($<type_enum>1);
|
$<type>$.typ = ECPGmake_simple_type($<type_enum>1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ECPGmake_record_member($<symbolname>2, ECPGmake_simple_type($<type_enum>1), &(record_member_list[struct_level-1]));
|
||||||
}
|
}
|
||||||
|
|
||||||
array_type : simple_tag symbol index {
|
array_type : simple_tag symbol index {
|
||||||
fprintf(yyout, "%s %s [%d]", ECPGtype_name($<type_enum>1), $<symbolname>2, $<indexsize>3);
|
fprintf(yyout, "%s %s [%d]", ECPGtype_name($<type_enum>1), $<symbolname>2, $<indexsize>3);
|
||||||
|
if (struct_level == 0)
|
||||||
|
{
|
||||||
$<type>$.name = $<symbolname>2;
|
$<type>$.name = $<symbolname>2;
|
||||||
$<type>$.typ = ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<indexsize>3);
|
$<type>$.typ = ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<indexsize>3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ECPGmake_record_member($<symbolname>2, ECPGmake_array_type(ECPGmake_simple_type($<type_enum>1), $<indexsize>3), &(record_member_list[struct_level-1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
s_struct : S_STRUCT symbol {
|
||||||
|
struct_level++;
|
||||||
|
fprintf(yyout, "struct %s {", $<symbolname>2);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_type : s_struct '{' variable_declarations '}' symbol {
|
||||||
|
struct_level--;
|
||||||
|
if (struct_level == 0)
|
||||||
|
{
|
||||||
|
$<type>$.name = $<symbolname>5;
|
||||||
|
$<type>$.typ = ECPGmake_record_type(record_member_list[struct_level]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ECPGmake_record_member($<symbolname>5, ECPGmake_record_type(record_member_list[struct_level]), &(record_member_list[struct_level-1]));
|
||||||
|
fprintf(yyout, "} %s", $<symbolname>5);
|
||||||
|
record_member_list[struct_level] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
simple_tag : S_CHAR { $<type_enum>$ = ECPGt_char; }
|
simple_tag : S_CHAR { $<type_enum>$ = ECPGt_char; }
|
||||||
@ -281,6 +325,9 @@ simple_tag : S_CHAR { $<type_enum>$ = ECPGt_char; }
|
|||||||
|
|
||||||
maybe_storage_clause : S_EXTERN { fwrite(yytext, yyleng, 1, yyout); }
|
maybe_storage_clause : S_EXTERN { fwrite(yytext, yyleng, 1, yyout); }
|
||||||
| S_STATIC { fwrite(yytext, yyleng, 1, yyout); }
|
| S_STATIC { fwrite(yytext, yyleng, 1, yyout); }
|
||||||
|
| S_CONST { fwrite(yytext, yyleng, 1, yyout); }
|
||||||
|
| S_REGISTER { fwrite(yytext, yyleng, 1, yyout); }
|
||||||
|
| S_AUTO { fwrite(yytext, yyleng, 1, yyout); }
|
||||||
| /* empty */ { };
|
| /* empty */ { };
|
||||||
|
|
||||||
index : '[' length ']' {
|
index : '[' length ']' {
|
||||||
@ -369,13 +416,14 @@ canything : both_anything
|
|||||||
sqlanything : both_anything;
|
sqlanything : both_anything;
|
||||||
|
|
||||||
both_anything : S_LENGTH | S_VARCHAR | S_VARCHAR2
|
both_anything : S_LENGTH | S_VARCHAR | S_VARCHAR2
|
||||||
| S_LONG | S_SHORT | S_INT | S_CHAR | S_FLOAT | S_DOUBLE
|
| S_LONG | S_SHORT | S_INT | S_CHAR | S_FLOAT | S_DOUBLE | S_BOOL
|
||||||
| SQL_OPEN | SQL_CONNECT
|
| SQL_OPEN | SQL_CONNECT
|
||||||
| SQL_STRING
|
| SQL_STRING
|
||||||
| SQL_BEGIN | SQL_END
|
| SQL_BEGIN | SQL_END
|
||||||
| SQL_DECLARE | SQL_SECTION
|
| SQL_DECLARE | SQL_SECTION
|
||||||
| SQL_INCLUDE
|
| SQL_INCLUDE
|
||||||
| S_SYMBOL
|
| S_SYMBOL
|
||||||
|
| S_STATIC | S_EXTERN | S_AUTO | S_CONST | S_REGISTER | S_STRUCT
|
||||||
| '[' | ']' | ','
|
| '[' | ']' | ','
|
||||||
| S_ANYTHING;
|
| S_ANYTHING;
|
||||||
|
|
||||||
|
@ -1,29 +1,51 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "type.h"
|
#include "type.h"
|
||||||
|
|
||||||
|
/* malloc + error check */
|
||||||
|
void *mm_alloc(size_t size)
|
||||||
|
{
|
||||||
|
void *ptr = malloc(size);
|
||||||
|
|
||||||
|
if (ptr == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out of memory\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Constructors
|
/* Constructors
|
||||||
Yes, I mostly write c++-code
|
Yes, I mostly write c++-code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* The NAME argument is copied. The type argument is preserved as a pointer. */
|
/* The NAME argument is copied. The type argument is preserved as a pointer. */
|
||||||
struct ECPGrecord_member *
|
struct ECPGrecord_member *
|
||||||
ECPGmake_record_member(char * name, struct ECPGtype * type)
|
ECPGmake_record_member(char *name, struct ECPGtype *type, struct ECPGrecord_member **start)
|
||||||
{
|
{
|
||||||
struct ECPGrecord_member * ne =
|
struct ECPGrecord_member *ptr, *ne =
|
||||||
(struct ECPGrecord_member *)malloc(sizeof(struct ECPGrecord_member));
|
(struct ECPGrecord_member *) mm_alloc(sizeof(struct ECPGrecord_member));
|
||||||
|
|
||||||
ne->name = strdup(name);
|
ne->name = strdup(name);
|
||||||
ne->typ = type;
|
ne->typ = type;
|
||||||
|
ne->next = NULL;
|
||||||
|
|
||||||
|
for (ptr = *start; ptr && ptr->next; ptr = ptr->next);
|
||||||
|
|
||||||
|
if (ptr)
|
||||||
|
ptr->next=ne;
|
||||||
|
else
|
||||||
|
*start=ne;
|
||||||
return ne;
|
return ne;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ECPGtype *
|
struct ECPGtype *
|
||||||
ECPGmake_simple_type(enum ECPGttype typ)
|
ECPGmake_simple_type(enum ECPGttype typ)
|
||||||
{
|
{
|
||||||
struct ECPGtype * ne = (struct ECPGtype *)malloc(sizeof(struct ECPGtype));
|
struct ECPGtype *ne = (struct ECPGtype *) mm_alloc(sizeof(struct ECPGtype));
|
||||||
|
|
||||||
ne->typ = typ;
|
ne->typ = typ;
|
||||||
ne->size = 0;
|
ne->size = 0;
|
||||||
@ -35,7 +57,7 @@ ECPGmake_simple_type(enum ECPGttype typ)
|
|||||||
struct ECPGtype *
|
struct ECPGtype *
|
||||||
ECPGmake_varchar_type(enum ECPGttype typ, unsigned short siz)
|
ECPGmake_varchar_type(enum ECPGttype typ, unsigned short siz)
|
||||||
{
|
{
|
||||||
struct ECPGtype * ne = ECPGmake_simple_type(typ);
|
struct ECPGtype *ne = ECPGmake_simple_type(typ);
|
||||||
|
|
||||||
ne->size = siz;
|
ne->size = siz;
|
||||||
|
|
||||||
@ -43,9 +65,9 @@ ECPGmake_varchar_type(enum ECPGttype typ, unsigned short siz)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ECPGtype *
|
struct ECPGtype *
|
||||||
ECPGmake_array_type(struct ECPGtype * typ, unsigned short siz)
|
ECPGmake_array_type(struct ECPGtype *typ, unsigned short siz)
|
||||||
{
|
{
|
||||||
struct ECPGtype * ne = ECPGmake_simple_type(ECPGt_array);
|
struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_array);
|
||||||
|
|
||||||
ne->size = siz;
|
ne->size = siz;
|
||||||
ne->u.element = typ;
|
ne->u.element = typ;
|
||||||
@ -54,9 +76,9 @@ ECPGmake_array_type(struct ECPGtype * typ, unsigned short siz)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ECPGtype *
|
struct ECPGtype *
|
||||||
ECPGmake_record_type(struct ECPGrecord_member * rm[])
|
ECPGmake_record_type(struct ECPGrecord_member *rm)
|
||||||
{
|
{
|
||||||
struct ECPGtype * ne = ECPGmake_simple_type(ECPGt_record);
|
struct ECPGtype *ne = ECPGmake_simple_type(ECPGt_record);
|
||||||
|
|
||||||
ne->u.members = rm;
|
ne->u.members = rm;
|
||||||
|
|
||||||
@ -78,25 +100,25 @@ ECPGmake_record_type(struct ECPGrecord_member * rm[])
|
|||||||
size is the maxsize in case it is a varchar. Otherwise it is the size of
|
size is the maxsize in case it is a varchar. Otherwise it is the size of
|
||||||
the variable (required to do array fetches of records).
|
the variable (required to do array fetches of records).
|
||||||
*/
|
*/
|
||||||
void ECPGdump_a_simple(FILE * o, const char * name, enum ECPGttype typ,
|
void ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
|
||||||
short varcharsize,
|
short varcharsize,
|
||||||
unsigned short arrsiz, const char * siz);
|
unsigned short arrsiz, const char *siz, const char *prefix);
|
||||||
void ECPGdump_a_record(FILE * o, const char * name, unsigned short arrsiz,
|
void ECPGdump_a_record(FILE *o, const char *name, unsigned short arrsiz,
|
||||||
struct ECPGtype * typ, const char * offset);
|
struct ECPGtype *typ, const char *offset, const char *prefix);
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ECPGdump_a_type(FILE * o, const char * name, struct ECPGtype * typ)
|
ECPGdump_a_type(FILE *o, const char *name, struct ECPGtype *typ, const char *prefix)
|
||||||
{
|
{
|
||||||
if (IS_SIMPLE_TYPE(typ->typ))
|
if (IS_SIMPLE_TYPE(typ->typ))
|
||||||
{
|
{
|
||||||
ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0);
|
ECPGdump_a_simple(o, name, typ->typ, typ->size, 0, 0, prefix);
|
||||||
}
|
}
|
||||||
else if (typ->typ == ECPGt_array)
|
else if (typ->typ == ECPGt_array)
|
||||||
{
|
{
|
||||||
if (IS_SIMPLE_TYPE(typ->u.element->typ))
|
if (IS_SIMPLE_TYPE(typ->u.element->typ))
|
||||||
ECPGdump_a_simple(o, name, typ->u.element->typ,
|
ECPGdump_a_simple(o, name, typ->u.element->typ,
|
||||||
typ->u.element->size, typ->size, 0);
|
typ->u.element->size, typ->size, 0, prefix);
|
||||||
else if (typ->u.element->typ == ECPGt_array)
|
else if (typ->u.element->typ == ECPGt_array)
|
||||||
{
|
{
|
||||||
abort(); /* Array of array, */
|
abort(); /* Array of array, */
|
||||||
@ -104,7 +126,7 @@ ECPGdump_a_type(FILE * o, const char * name, struct ECPGtype * typ)
|
|||||||
else if (typ->u.element->typ == ECPGt_record)
|
else if (typ->u.element->typ == ECPGt_record)
|
||||||
{
|
{
|
||||||
/* Array of records. */
|
/* Array of records. */
|
||||||
ECPGdump_a_record(o, name, typ->size, typ->u.element, 0);
|
ECPGdump_a_record(o, name, typ->size, typ->u.element, 0, prefix);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -113,7 +135,7 @@ ECPGdump_a_type(FILE * o, const char * name, struct ECPGtype * typ)
|
|||||||
}
|
}
|
||||||
else if (typ->typ == ECPGt_record)
|
else if (typ->typ == ECPGt_record)
|
||||||
{
|
{
|
||||||
ECPGdump_a_record(o, name, 0, typ, 0);
|
ECPGdump_a_record(o, name, 0, typ, 0, prefix);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -125,68 +147,69 @@ ECPGdump_a_type(FILE * o, const char * name, struct ECPGtype * typ)
|
|||||||
/* If siz is NULL, then the offset is 0, if not use siz as a
|
/* If siz is NULL, then the offset is 0, if not use siz as a
|
||||||
string, it represents the offset needed if we are in an array of records. */
|
string, it represents the offset needed if we are in an array of records. */
|
||||||
void
|
void
|
||||||
ECPGdump_a_simple(FILE * o, const char * name, enum ECPGttype typ,
|
ECPGdump_a_simple(FILE *o, const char *name, enum ECPGttype typ,
|
||||||
short varcharsize,
|
short varcharsize,
|
||||||
unsigned short arrsiz,
|
unsigned short arrsiz,
|
||||||
const char * siz)
|
const char *siz,
|
||||||
|
const char *prefix)
|
||||||
{
|
{
|
||||||
switch (typ)
|
switch (typ)
|
||||||
{
|
{
|
||||||
case ECPGt_char:
|
case ECPGt_char:
|
||||||
fprintf(o, "\n\tECPGt_char,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_char,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(char)" : siz);
|
siz == NULL ? "sizeof(char)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_unsigned_char:
|
case ECPGt_unsigned_char:
|
||||||
fprintf(o, "\n\tECPGt_unsigned_char,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_unsigned_char,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(unsigned char)" : siz);
|
siz == NULL ? "sizeof(unsigned char)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_short:
|
case ECPGt_short:
|
||||||
fprintf(o, "\n\tECPGt_short,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_short,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(short)" : siz);
|
siz == NULL ? "sizeof(short)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_unsigned_short:
|
case ECPGt_unsigned_short:
|
||||||
fprintf(o,
|
fprintf(o,
|
||||||
"\n\tECPGt_unsigned_short,&%s,0,%d,%s, ", name, arrsiz,
|
"\n\tECPGt_unsigned_short,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(unsigned short)" : siz);
|
siz == NULL ? "sizeof(unsigned short)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_int:
|
case ECPGt_int:
|
||||||
fprintf(o, "\n\tECPGt_int,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_int,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(int)" : siz);
|
siz == NULL ? "sizeof(int)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_unsigned_int:
|
case ECPGt_unsigned_int:
|
||||||
fprintf(o, "\n\tECPGt_unsigned_int,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_unsigned_int,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(unsigned int)" : siz);
|
siz == NULL ? "sizeof(unsigned int)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_long:
|
case ECPGt_long:
|
||||||
fprintf(o, "\n\tECPGt_long,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_long,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(long)" : siz);
|
siz == NULL ? "sizeof(long)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_unsigned_long:
|
case ECPGt_unsigned_long:
|
||||||
fprintf(o, "\n\tECPGt_unsigned_int,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_unsigned_int,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(unsigned int)" : siz);
|
siz == NULL ? "sizeof(unsigned int)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_float:
|
case ECPGt_float:
|
||||||
fprintf(o, "\n\tECPGt_float,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_float,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(float)" : siz);
|
siz == NULL ? "sizeof(float)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_double:
|
case ECPGt_double:
|
||||||
fprintf(o, "\n\tECPGt_double,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_double,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(double)" : siz);
|
siz == NULL ? "sizeof(double)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_bool:
|
case ECPGt_bool:
|
||||||
fprintf(o, "\n\tECPGt_bool,&%s,0,%d,%s, ", name, arrsiz,
|
fprintf(o, "\n\tECPGt_bool,&%s%s,0,%d,%s, ", prefix ? prefix : "", name, arrsiz,
|
||||||
siz == NULL ? "sizeof(bool)" : siz);
|
siz == NULL ? "sizeof(bool)" : siz);
|
||||||
break;
|
break;
|
||||||
case ECPGt_varchar:
|
case ECPGt_varchar:
|
||||||
case ECPGt_varchar2:
|
case ECPGt_varchar2:
|
||||||
if (siz == NULL)
|
if (siz == NULL)
|
||||||
fprintf(o, "\n\tECPGt_varchar,&%s,%d,%d,sizeof(struct varchar_%s), ",
|
fprintf(o, "\n\tECPGt_varchar,&%s%s,%d,%d,sizeof(struct varchar_%s), ",
|
||||||
name,
|
prefix ? prefix : "", name,
|
||||||
varcharsize,
|
varcharsize,
|
||||||
arrsiz, name);
|
arrsiz, name);
|
||||||
else
|
else
|
||||||
fprintf(o, "\n\tECPGt_varchar,&%s,%d,%d,%s, ",
|
fprintf(o, "\n\tECPGt_varchar,&%s%s,%d,%d,%s, ",
|
||||||
name,
|
prefix ? prefix : "", name,
|
||||||
varcharsize,
|
varcharsize,
|
||||||
arrsiz, siz);
|
arrsiz, siz);
|
||||||
break;
|
break;
|
||||||
@ -198,17 +221,15 @@ ECPGdump_a_simple(FILE * o, const char * name, enum ECPGttype typ,
|
|||||||
|
|
||||||
/* Penetrate a record and dump the contents. */
|
/* Penetrate a record and dump the contents. */
|
||||||
void
|
void
|
||||||
ECPGdump_a_record(FILE * o,
|
ECPGdump_a_record(FILE *o, const char *name, unsigned short arrsiz, struct ECPGtype *typ, const char *offsetarg, const char *prefix)
|
||||||
const char * name, unsigned short arrsiz,
|
|
||||||
struct ECPGtype * typ, const char * offsetarg)
|
|
||||||
{
|
{
|
||||||
/* If offset is NULL, then this is the first recursive level. If not then
|
/* If offset is NULL, then this is the first recursive level. If not then
|
||||||
we are in a record in a record and the offset is used as offset.
|
we are in a record in a record and the offset is used as offset.
|
||||||
*/
|
*/
|
||||||
struct ECPGrecord_member ** p;
|
struct ECPGrecord_member *p;
|
||||||
char obuf[BUFSIZ];
|
char obuf[BUFSIZ];
|
||||||
char buf[BUFSIZ];
|
char pbuf[BUFSIZ];
|
||||||
const char * offset;
|
const char *offset;
|
||||||
|
|
||||||
if (offsetarg == NULL)
|
if (offsetarg == NULL)
|
||||||
{
|
{
|
||||||
@ -220,32 +241,37 @@ ECPGdump_a_record(FILE * o,
|
|||||||
offset = offsetarg;
|
offset = offsetarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (p = typ->u.members; *p; p++)
|
sprintf(pbuf, "%s%s.", prefix ? prefix : "", name);
|
||||||
|
prefix = pbuf;
|
||||||
|
|
||||||
|
for (p = typ->u.members; p; p=p->next)
|
||||||
{
|
{
|
||||||
if (IS_SIMPLE_TYPE((*p)->typ->typ))
|
#if 0
|
||||||
|
if (IS_SIMPLE_TYPE(p->typ->typ))
|
||||||
{
|
{
|
||||||
sprintf(buf, "%s.%s", name, (*p)->name);
|
sprintf(buf, "%s.%s", name, p->name);
|
||||||
ECPGdump_a_simple(o, buf, (*p)->typ->typ, (*p)->typ->size,
|
ECPGdump_a_simple(o, buf, p->typ->typ, p->typ->size,
|
||||||
arrsiz, offset);
|
arrsiz, offset);
|
||||||
}
|
}
|
||||||
else if ((*p)->typ->typ == ECPGt_array)
|
else if (p->typ->typ == ECPGt_array)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < (*p)->typ->size; i++)
|
for (i = 0; i < p->typ->size; i++)
|
||||||
{
|
{
|
||||||
if (IS_SIMPLE_TYPE((*p)->typ->u.element->typ))
|
if (IS_SIMPLE_TYPE(p->typ->u.element->typ))
|
||||||
{
|
{
|
||||||
sprintf(buf, "%s.%s[%d]", name, (*p)->name, i);
|
/* sprintf(buf, "%s.%s[%d]", name, p->name, i); */
|
||||||
ECPGdump_a_simple(o, buf, (*p)->typ->typ, (*p)->typ->size,
|
sprintf(buf, "%s.%s", name, p->name);
|
||||||
arrsiz, offset);
|
ECPGdump_a_simple(o, buf, p->typ->u.element->typ, p->typ->u.element->size,
|
||||||
|
p->typ->u.element->size, offset);
|
||||||
}
|
}
|
||||||
else if((*p)->typ->u.element->typ == ECPGt_array)
|
else if (p->typ->u.element->typ == ECPGt_array)
|
||||||
{
|
{
|
||||||
/* Array within an array. NOT implemented yet. */
|
/* Array within an array. NOT implemented. */
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
else if ((*p)->typ->u.element->typ == ECPGt_record)
|
else if (p->typ->u.element->typ == ECPGt_record)
|
||||||
{
|
{
|
||||||
/* Record within array within record. NOT implemented yet. */
|
/* Record within array within record. NOT implemented yet. */
|
||||||
abort();
|
abort();
|
||||||
@ -257,17 +283,19 @@ ECPGdump_a_record(FILE * o,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((*p)->typ->typ == ECPGt_record)
|
else if (p->typ->typ == ECPGt_record)
|
||||||
{
|
{
|
||||||
/* Record within a record */
|
/* Record within a record */
|
||||||
sprintf(buf, "%s.%s", name, (*p)->name);
|
sprintf(buf, "%s.%s", name, p->name);
|
||||||
ECPGdump_a_record(o, buf, arrsiz, (*p)->typ, offset);
|
ECPGdump_a_record(o, buf, arrsiz, p->typ, offset);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Unknown type */
|
/* Unknown type */
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
ECPGdump_a_type(o, p->name, p->typ, prefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,11 +304,43 @@ ECPGdump_a_record(FILE * o,
|
|||||||
anyway. Lets implement that last! */
|
anyway. Lets implement that last! */
|
||||||
|
|
||||||
void
|
void
|
||||||
ECPGfree_record_member(struct ECPGrecord_member * rm)
|
ECPGfree_record_member(struct ECPGrecord_member *rm)
|
||||||
{
|
{
|
||||||
|
while (rm)
|
||||||
|
{
|
||||||
|
struct ECPGrecord_member *p = rm;
|
||||||
|
|
||||||
|
rm = rm->next;
|
||||||
|
free(p->name);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ECPGfree_type(struct ECPGtype * typ)
|
ECPGfree_type(struct ECPGtype *typ)
|
||||||
{
|
{
|
||||||
|
if (!IS_SIMPLE_TYPE(typ->typ))
|
||||||
|
{
|
||||||
|
if (typ->typ == ECPGt_array)
|
||||||
|
{
|
||||||
|
if (IS_SIMPLE_TYPE(typ->u.element->typ))
|
||||||
|
free(typ->u.element);
|
||||||
|
else if (typ->u.element->typ == ECPGt_array)
|
||||||
|
abort(); /* Array of array, */
|
||||||
|
else if (typ->u.element->typ == ECPGt_record)
|
||||||
|
/* Array of records. */
|
||||||
|
ECPGfree_record_member(typ->u.members);
|
||||||
|
else
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
else if (typ->typ == ECPGt_record)
|
||||||
|
{
|
||||||
|
ECPGfree_record_member(typ->u.members);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(typ);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@ struct ECPGtype;
|
|||||||
struct ECPGrecord_member {
|
struct ECPGrecord_member {
|
||||||
char * name;
|
char * name;
|
||||||
struct ECPGtype * typ;
|
struct ECPGtype * typ;
|
||||||
|
struct ECPGrecord_member * next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ECPGtype {
|
struct ECPGtype {
|
||||||
enum ECPGttype typ;
|
enum ECPGttype typ;
|
||||||
unsigned short size; /* For array it is the number of elements.
|
unsigned short size; /* For array it is the number of elements.
|
||||||
@ -14,17 +16,17 @@ struct ECPGtype {
|
|||||||
struct ECPGtype * element; /* For an array this is the type of the
|
struct ECPGtype * element; /* For an array this is the type of the
|
||||||
* element */
|
* element */
|
||||||
|
|
||||||
struct ECPGrecord_member ** members;
|
struct ECPGrecord_member * members;
|
||||||
/* A pointer to an array of members. */
|
/* A pointer to a list of members. */
|
||||||
} u;
|
} u;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Everything is malloced. */
|
/* Everything is malloced. */
|
||||||
struct ECPGrecord_member * ECPGmake_record_member(char *, struct ECPGtype *);
|
struct ECPGrecord_member * ECPGmake_record_member(char *, struct ECPGtype *, struct ECPGrecord_member **);
|
||||||
struct ECPGtype * ECPGmake_simple_type(enum ECPGttype);
|
struct ECPGtype * ECPGmake_simple_type(enum ECPGttype);
|
||||||
struct ECPGtype * ECPGmake_varchar_type(enum ECPGttype, unsigned short);
|
struct ECPGtype * ECPGmake_varchar_type(enum ECPGttype, unsigned short);
|
||||||
struct ECPGtype * ECPGmake_array_type(struct ECPGtype *, unsigned short);
|
struct ECPGtype * ECPGmake_array_type(struct ECPGtype *, unsigned short);
|
||||||
struct ECPGtype * ECPGmake_record_type(struct ECPGrecord_member *[]);
|
struct ECPGtype * ECPGmake_record_type(struct ECPGrecord_member *);
|
||||||
|
|
||||||
/* Frees a type. */
|
/* Frees a type. */
|
||||||
void ECPGfree_record_member(struct ECPGrecord_member *);
|
void ECPGfree_record_member(struct ECPGrecord_member *);
|
||||||
@ -40,7 +42,7 @@ void ECPGfree_type(struct ECPGtype *);
|
|||||||
size is the maxsize in case it is a varchar. Otherwise it is the size of
|
size is the maxsize in case it is a varchar. Otherwise it is the size of
|
||||||
the variable (required to do array fetches of records).
|
the variable (required to do array fetches of records).
|
||||||
*/
|
*/
|
||||||
void ECPGdump_a_type(FILE *, const char * name, struct ECPGtype *);
|
void ECPGdump_a_type(FILE *, const char * name, struct ECPGtype *, const char *);
|
||||||
|
|
||||||
/* A simple struct to keep a variable and its type. */
|
/* A simple struct to keep a variable and its type. */
|
||||||
struct ECPGtemp_type {
|
struct ECPGtemp_type {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
create table meskes(name char8, born int4);
|
create table meskes(name char8, born int4, age int2);
|
||||||
|
|
||||||
insert into meskes(name, born) values ('Petra', 19661202);
|
insert into meskes(name, born) values ('Petra', 19661202, 31);
|
||||||
insert into meskes(name, born) values ('Michael', 19660117);
|
insert into meskes(name, born) values ('Michael', 19660117, 32);
|
||||||
insert into meskes(name, born) values ('Carsten', 19910103);
|
insert into meskes(name, born) values ('Carsten', 19910103, 7);
|
||||||
insert into meskes(name, born) values ('Marc', 19930907);
|
insert into meskes(name, born) values ('Marc', 19930907, 4);
|
||||||
insert into meskes(name, born) values ('Chris', 19970923);
|
insert into meskes(name, born) values ('Chris', 19970923, 0);
|
||||||
|
|
||||||
|
@ -18,8 +18,11 @@ int
|
|||||||
main ()
|
main ()
|
||||||
{
|
{
|
||||||
exec sql begin declare section;
|
exec sql begin declare section;
|
||||||
varchar name[8];
|
struct personal_struct { varchar name[8];
|
||||||
long born;
|
struct birth_struct { long born;
|
||||||
|
short age;
|
||||||
|
} birth;
|
||||||
|
} personal;
|
||||||
exec sql end declare section;
|
exec sql end declare section;
|
||||||
FILE *dbgs;
|
FILE *dbgs;
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ exec sql end declare section;
|
|||||||
db_error ("connect");
|
db_error ("connect");
|
||||||
|
|
||||||
exec sql declare cur cursor for
|
exec sql declare cur cursor for
|
||||||
select name, born from meskes;
|
select name, born, age from meskes;
|
||||||
if (SQLCODE) db_error ("declare");
|
if (SQLCODE) db_error ("declare");
|
||||||
|
|
||||||
exec sql open cur;
|
exec sql open cur;
|
||||||
@ -39,10 +42,10 @@ exec sql end declare section;
|
|||||||
db_error ("open");
|
db_error ("open");
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
exec sql fetch in cur into :name, :born;
|
exec sql fetch in cur into :personal;
|
||||||
if (SQLCODE)
|
if (SQLCODE)
|
||||||
break;
|
break;
|
||||||
printf ("%8.8s was born %d\n", name.arr, born);
|
printf ("%8.8s was born %d (age = %d)\n", personal.name.arr, personal.birth.born, personal.birth.age);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SQLCODE < 0)
|
if (SQLCODE < 0)
|
||||||
|
@ -43,9 +43,6 @@ foo.bar.
|
|||||||
.BR "file1, file2, ..."
|
.BR "file1, file2, ..."
|
||||||
The files to be processed.
|
The files to be processed.
|
||||||
.SH "BUGS"
|
.SH "BUGS"
|
||||||
This version of ecpg is not able to handle structures inside the sql declare
|
|
||||||
blocks.
|
|
||||||
.TP
|
|
||||||
The return code is alway -1 in case of an error. You cannot see which error
|
The return code is alway -1 in case of an error. You cannot see which error
|
||||||
occured by examining the return code.
|
occured by examining the return code.
|
||||||
.SH "RETURN VALUE"
|
.SH "RETURN VALUE"
|
||||||
|
Reference in New Issue
Block a user