/* report.c Copyright (c) Kapteyn Laboratorium Groningen 1991, 1997, 2000 All Rights Reserved. #> report.dc1 Document: report Purpose: reports a bug or a wish Category: UTILITY File: report.c Author: W. Zwitser Keywords: SUBJECT= name of a task (usually) A report may also be given about: - "general", if it concerns a general Gipsy item - a subroutine or document. SUMMARY= a summary of the report ( max. 50 characters ) [quit] TYPE= type of report [BUG] BUG : if i.e. the program crashes or gives wrong results WISH: if it concerns i.e. a new feature ** FILE= name of a prepared bug report ** EDIT= Y: edit the prepared bug report [N] N: bypass the edit session (only if FILE= was specified) Use: After the mentioned keywords the report is created in an edit session. The environment variable EDITOR is used to determine the editor. Just before the editor is started it gives the name of a file on /tmp which is used to create the report. This is not meant to change that name but to have the opportunity to abort with ^C. An error message about the editor generally means that you did not define the environment variable EDITOR. Updates: May 3, 1992: WZ test version installed. May 4, 1992: WZ version+date of subject added to report. Aug 24, 1993: JPT bitmap logic for fixes corrected. Nov 19, 1993: KGB bug in clearstr repaired. Apr 12, 1994: KGB address of GIPSY server now from server.mgr. May 12, 1997: KGB default address changed to new server. Jan 16, 1999: JPT modified for Linux 2.0.36 Jun 7, 2000: JPT simplified; disabled bug listings Mar 9, 2001: JPT changed e-mail address Feb 11, 2004: JPT included subject in mail May 4, 2007: JPT included conditional code for Apple Mac. #< */ /* report: include files */ #define SIMPLIFIED 1 #include "cmain.h" #include "ctype.h" #include "stdio.h" #include "stdlib.h" #include "stddef.h" #if defined(__linux__) | defined(__APPLE__) #include #endif #include "string.h" #include "time.h" #include "xscanf.h" /* code for xscanf */ #include "anyout.h" #include "cancel.h" #include "editfile.h" #include "error.h" #include "gipsyc.h" #include "init.h" #include "finis.h" #include "flist.h" #include "nelc.h" #include "sortiai.h" #include "userchar.h" #include "userint.h" #include "userlog.h" #include "usertext.h" #define getpwuid GETPWUID #include #undef getpwuid /* Malloc version of 'fmake' */ #define finit( fc , len ) { fc.a = malloc( ( len + 1 ) * sizeof( char ) ) ; \ fc.a[ len ] = '\0' ; \ fc.l = len ; } /* report: definitions */ #define KEY_LEN 20 #define MAX_REPS 512 #define MAX_CMD 255 #define MAX_DATE 40 #define MAX_LINE 255 #define MAX_NAME 80 #define MAX_SUBJECT 40 #define MAX_SUMMARY 50 #define MAX_TYPE 5 #define MES_LEN 100 #define RELEASE "1.0" #define TEXT_LEN 255 #define FATAL 0xffffffff /* fatal error */ #define F_OK 0 /* check presence of file */ #define KEY_EDIT tofchar("EDIT=") #define KEY_FIX tofchar("FIX=") #define KEY_FIXNR tofchar("FIXNR=") #define KEY_FILE tofchar("FILE=") #define KEY_MODIFY tofchar("MODIFY=") #define KEY_MODNR tofchar("MODNR=") #define KEY_READNR tofchar("READNR=") #define KEY_SUBJECT tofchar("SUBJECT=") #define KEY_SUMMARY tofchar("SUMMARY=") #define KEY_TYPE tofchar("TYPE=") #define MES_EDIT tofchar(" ") #define MES_FIX tofchar(" ") #define MES_FILE tofchar(" ") #define MES_FIXNR tofchar("give number of report which has been fixed [none]") #define MES_MODIFY tofchar(" ") #define MES_MODNR tofchar("give number of report to modify [none]") #define MES_READNR tofchar("give number of report to read [none] ") #define MES_SUBJECT_BUG tofchar("name of a task (usually)") #define MES_SUBJECT_FIX tofchar("name of a task for which a bug has been fixed [list all]") #define MES_SUBJECT_BUG_STOP tofchar("name of a task (usually) [quit]") #define MES_SUBJECT_FIX_STOP tofchar("name of a task for which a bug has been fixed [quit]") #define MES_SUMMARY tofchar("give a summary of the problem (max.50 characters) [quit]") #define MES_TYPE tofchar("type of report [BUG]") #if !defined(__linux__) & !defined(__APPLE__) extern int access( char *path, int amode ); #endif /* report: variables */ static char date[MAX_DATE]; static char *extension = ".rep"; static char *fix_begin = "Gipsy_Fix_Report_Begin"; static char *fix_end = "Gipsy_Fix_Report_End"; static char *mod_begin = "Gipsy_Mod_Report_Begin"; static char *rep_begin = "Gipsy_Report_Begin"; static char *rep_end = "Gipsy_Report_End"; static char gip_doc[FILENAME_MAX]; static char gip_mis[FILENAME_MAX]; static char gip_sub[FILENAME_MAX]; static char gip_tsk[FILENAME_MAX]; static char edit_file[FILENAME_MAX]; static fchar edit_file_f = { edit_file, FILENAME_MAX }; static fchar Key, Mes; static char inst_date[MAX_DATE]; static char mail_file[FILENAME_MAX]; static char subject[MAX_SUBJECT]; static fchar subject_f = { subject, MAX_SUBJECT }; static char *subject_label = "Subject#"; static char *summary_label = "Summary#"; static char text[MES_LEN]; static fchar text_f = { text, MES_LEN }; static char *version_label = "Version#"; static fint dev = 0; static fint fatal = 4; static fint deflt = 1; static fint hidden = 2; static fint lsubject = 0; static fint nreps = 0; static int level; static int version; #if !defined(__linux__) & !defined(__APPLE__) extern uid_t getuid( void ); #endif typedef struct { char subject[MAX_SUBJECT]; int level; int nreports; int fixed[10]; /* a maximum of 320 reports/subject possible */ } rep_struct; static rep_struct *rep = NULL; /* * clearstr fills a string with l blanks */ static void clearstr( fchar str, fint l ) { int i; for (i = 0; i < (int) l; str.a[i++] = ' ' ); str.a[l-1] = '\0'; } /* * getuser obtains the name of the user. */ static void getuser( char *username ) { char *getlogin( ); /* gets login name */ char *uname; /* points to login name */ uname = getlogin( ); /* obtain login name */ if (uname == NULL) { /* no success */ struct passwd *pw, *getpwuid( ); /* from /etc/passwd */ pw = getpwuid( getuid( ) ); /* get pw struct */ uname = pw->pw_name; /* get login name */ } strcpy( username, uname ); /* copy user name */ } /* * compare sorts reports at 2 "columns": * - level * - within level at subject */ static int compare( const void *a1, const void *a2 ) { const rep_struct *b1 = a1; const rep_struct *b2 = a2; int level = b1->level - b2->level; return( level == 0 ) ? strcmp( b1->subject, b2->subject ) : level; } /* * docmd does the unix command. It returns FATAL when the command could * not be executed, otherwize the status of the command is returned. */ static int docmd( char *cmd ) { int r; /* return from call to system */ r = system( cmd ); /* system call */ return( r ); /* return to caller */ } /* * mail_report sends the report file to the "gipsy" mailbox */ static int mail_report( ) { char cmd[MAX_CMD]; char gipsy_root[MAX_NAME]; char gipsy_mail[MAX_NAME]; char serv_file[FILENAME_MAX]; char gipsy_serv[MAX_NAME]; FILE *fp_serv; int r = 0; sprintf( serv_file, "%s/server", getenv( "gip_loc" ) ); fp_serv = fopen( serv_file, "r" ); if ( fp_serv == NULL ) { sprintf( serv_file, "%s/server.mgr", getenv( "gip_sys" ) ); fp_serv = fopen( serv_file, "r" ); } if ( fp_serv != NULL ) { r = xscanf( fp_serv, "%s %s %s", gipsy_serv, gipsy_mail, gipsy_root ); } if ( r != 3 ) { strcpy( gipsy_mail, "gipsy@astro.rug.nl" ); } if ( fp_serv != NULL ) fclose( fp_serv ); sprintf( cmd, "mail -s \"GIPSY BUG REPORT\" %s < %s", gipsy_mail, mail_file ); return( docmd( cmd ) ); } /* * rep_scanf scans the first line of a bug report */ static void rep_scanf( char *line, int *level, int *nr, char *type, char *date_send, char *date_fix, char *user_mail_addr, char *resp_mail_addr ) { char string[5]; int it = 0, iline = 0; int item = 0; date_fix[0] = '\0'; while( line[iline] != '\n' ) { if ( line[iline] == '#' ) { int l = iline - it; switch( item ) { case 0: { /* label: Gipsy_Report_Begin */ break; } case 1: { /* documentation level */ strncpy( string, line+it, l ); string[l] = '\0'; sscanf( string, "%d", level ); break; } case 2: { /* report number */ strncpy( string, line+it, l ); string[l] = '\0'; sscanf( string, "%d", nr ); break; } case 3: { /* type */ strncpy( type, line+it, l ); type[l] = '\0'; break; } case 4: { /* date send */ strncpy( date_send, line+it, l ); date_send[l] = '\0'; break; } case 5: { /* date fixed */ strncpy( date_fix, line+it, l ); date_fix[l] = '\0'; break; } case 6: { /* user mail address */ strncpy( user_mail_addr, line+it, l ); user_mail_addr[l] = '\0'; break; } case 7: { /* responsible person */ strncpy( resp_mail_addr, line+it, l ); resp_mail_addr[l] = '\0'; break; } } it = iline + 1; item++; } iline++; } } /* * read_report reads a report from a ".rep"-file and prints the header * information in the screen/log file. If contents == 1 the * report itself is printed too. * It returns 1 if a report was found, otherwise 0. */ static int read_report( FILE *fp_rep, int irep, fint report, int contents ) { char date_fix[MAX_DATE]; char date_send[MAX_DATE]; char resp_mail_addr[MAX_NAME]; char user_mail_addr[MAX_NAME]; char summary[MAX_SUMMARY]; char line[MAX_LINE]; char type[MAX_TYPE]; int level, nr, ok = 0; #if SIMPLIFIED return 0; #endif while( fgets( line, MAX_LINE, fp_rep ) ) { if( strstr( line, rep_begin ) != NULL ) { /* header information */ rep_scanf( line, &level, &nr, type, date_send, date_fix, user_mail_addr, resp_mail_addr ); if( nr == report || report == 0 ) ok = 1; if( ok ) { sprintf( text, "%4d %4s %s %-50.50s %13s\n", nr, type, "from", user_mail_addr, date_send ); anyout_c( &dev,text_f ); if( strlen( date_fix ) ) { int nbits = sizeof( rep[irep].fixed[0] ) * 8; int ilong = (report-1) / nbits; int ibit = (report-1) % nbits; rep[irep].fixed[ilong] |= (1<= 0 ) { char SUBJECT[MAX_SUBJECT]; for( i = 0; i < lsubject; i++ ) SUBJECT[i] = toupper( subject[i] ); SUBJECT[lsubject] = '\0'; fprintf( fp_mail, "%s(%s Version %d, installed at %s)\n", version_label, SUBJECT, version, inst_date ); } fprintf( fp_mail, "%s%.*s\n", summary_label, lsummary, summary ); while( fgets( line, MAX_LINE, fp_edit ) != NULL ) fputs( line, fp_mail ); fputs( rep_end, fp_mail ); fclose( fp_mail ); fclose( fp_edit ); r = mail_report( ); } } if( !access( mail_file, F_OK ) ) r = remove( mail_file ); if( !access( edit_file, F_OK ) && !ledit ) r = remove( edit_file ); } /* * mod_rep modifies an existing report */ static void mod_rep( ) { char rep_file[FILENAME_MAX]; FILE *fp_mail, *fp_edit = NULL, *fp_rep; fint report; fint nitems = 1; int irep; int known_report = 0, r; for( irep = 0; irep < nreps; irep++ ) { if( !strcmp( rep[irep].subject, subject ) ) { Key = KEY_MODNR; Mes = MES_MODNR; while( !known_report ) { r = userint_c( &report, &nitems, &deflt, Key, Mes ); cancel_c( Key ); if( r ) { if( report > rep[irep].nreports || report < 1 ) { sprintf( text, "report nr %d for \"%.*s\" not found\n", report, lsubject, subject ); anyout_c( &dev, text_f ); } else { known_report = report; } } else { break; } } } } if( known_report ) { char date_fix[MAX_DATE]; char date_send[MAX_DATE]; char resp_mail_addr[MAX_NAME]; char user_mail_addr[MAX_NAME]; char type[MAX_TYPE]; char line[MAX_LINE]; fint edit_ok; int level, nr, ok = 0; sprintf( rep_file, "%s/%s%s", gip_mis, subject, extension ); fp_rep = fopen( rep_file, "r" ); fp_edit = fopen( edit_file, "w" ); while( fgets( line, MAX_LINE, fp_rep ) ) { if( strstr( line, rep_begin ) != NULL ) { rep_scanf( line, &level, &nr, type, date_send, date_fix, user_mail_addr, resp_mail_addr ); if( nr == report ) { ok = 1; fprintf( fp_edit, "%s#%d#%d#%s#%s#%s#%s#%s#\n", mod_begin, level, nr, type, date_send, date_fix, user_mail_addr, resp_mail_addr ); } } else if( ( strstr( line, rep_end ) != NULL ) && ok ) { fputs( line, fp_edit ); break; } else if( ok ) { fputs( line, fp_edit ); } } fclose( fp_edit ); sprintf( text, "abort with ^C [ edit %s ]", edit_file ); edit_ok = editfile_c( tofchar( edit_file ), text_f ); fp_mail = fopen( mail_file, "w" ); fp_edit = fopen( edit_file, "r" ); if( fp_edit ) { char line[MAX_LINE]; if ( fp_mail == NULL ) { error_c( &fatal, tofchar( "could not open a report on /tmp" ) ); } fgets( line, MAX_LINE, fp_edit ); fputs( line, fp_mail ); fprintf( fp_mail, "%s%s\n", subject_label, subject ); while( fgets( line, MAX_LINE, fp_edit ) != NULL ) fputs( line, fp_mail ); fclose( fp_mail ); fclose( fp_edit ); r = mail_report( ); } } if( !access( mail_file, F_OK ) ) r = remove( mail_file ); if( !access( edit_file, F_OK ) ) r = remove( edit_file ); } /* * fix_rep mails the solution to the Gipsy manager */ static void fix_rep( ) { FILE *fp_mail, *fp_edit = NULL; fint report; fint ledit = 0, nitems = 1; int irep; int known_report = 0, r; for( irep = 0; irep < nreps; irep++ ) { if( !strcmp( rep[irep].subject, subject ) ) { Key = KEY_FIXNR; Mes = MES_FIXNR; while( !known_report ) { r = userint_c( &report, &nitems, &deflt, Key, Mes ); cancel_c( Key ); if( r ) { int nbits = sizeof( rep[irep].fixed[0] ) * 8; int ilong = (report-1) / nbits; int ibit = (report-1) % nbits; if( report > rep[irep].nreports || report < 1 ) { sprintf( text, "report nr %d for \"%.*s\" not found\n", report, lsubject, subject ); anyout_c( &dev, text_f ); } else { if( rep[irep].fixed[ilong] & (1< version ) { version = vers; strcpy( inst_date, his_date ); } } fclose( fp_his ); } } else { ok = ( !strcmp( subject, "general" ) ); } if( !ok ) { sprintf( text, "%s is unknown within Gipsy; report only about a documented feature", subject ); anyout_c( &dev, text_f ); } } return( ok ); } MAIN_PROGRAM_ENTRY { bool fix, modify; fint nitems = 1; int i, all_listed; time_t now = time( NULL ); init_c( ); IDENTIFICATION( "REPORT", RELEASE ); strcpy( gip_doc, getenv( "gip_doc" ) ); strcpy( gip_mis, getenv( "gip_mis" ) ); strcpy( gip_sub, getenv( "gip_sub" ) ); strcpy( gip_tsk, getenv( "gip_tsk" ) ); sprintf( mail_file, "/tmp/report.%d", (int) now ); sprintf( edit_file, "/tmp/edit.%d", (int) now ); strftime( date, MAX_DATE, "%b %d, %Y", localtime( &now ) ); fix = FALSE; modify = FALSE; #if !SIMPLIFIED finit( Key, KEY_LEN ); finit( Mes, MES_LEN ); Key = KEY_FIX; Mes = MES_FIX; userlog_c( &fix, &nitems, &hidden, Key, Mes ); Key = KEY_MODIFY; Mes = MES_MODIFY; userlog_c( &modify, &nitems, &hidden, Key, Mes ); #endif if( tobool( modify ) ) { char *manager[] = { "kgb", "terlouw", "zwitser" }; char username[20]; int ok = 0; getuser( username ); for( i = 0; i < 3; i++ ) if( !strcmp( username, manager[i] ) ) ok = 1; if( !ok ) { error_c( &fatal, tofchar( "you are not allowed to modify a report" ) ); } } for( all_listed = 0; all_listed < 2 && !lsubject; all_listed++ ) { Key = KEY_SUBJECT; if( tobool(fix) ) { if( all_listed ) { Mes = MES_SUBJECT_FIX_STOP; } else { Mes = MES_SUBJECT_FIX; } } else { if( all_listed ) { Mes = MES_SUBJECT_BUG_STOP; } else { Mes = MES_SUBJECT_BUG; } } do { fint nitems = 1; clearstr(subject_f, MAX_SUBJECT ); lsubject = userchar_c( subject_f, &nitems, &deflt, Key, Mes ); if (lsubject) lsubject = nelc_c( subject_f ); cancel_c( Key ); for( i = 0; i < lsubject; i++ ) { subject[i] = tolower( subject[i] ); } subject[lsubject] = '\0'; } while ( !subject_ok( ) && lsubject ); #if SIMPLIFIED known_reps( all_listed ); /* summary of known bugs */ #endif } if ( lsubject ) { if( tobool( modify ) ) { mod_rep( ); /* modify a report */ } else if( tobool( fix ) ) { fix_rep( ); /* a fix report */ } else { new_rep( ); /* a new report */ } } finis_c( ); return( 0 ); }