tc(' ', fp); break; } putc(cprev = c, fp); /* sama bukva */ state = TEXT; if(c == '\\') putc('e', fp); } s++; } /* probely v konce stroki prosto ignoriruyutsya */ putc ('\n', fp); } /* Obrabotat' fajl s imenem name */ void proceed (char *name) { FILE *fp; static unsigned char inp[2048]; /* dostatochno bol'shoj bufer vvoda */ if (strcmp(name, "-") == 0 ) fp = stdin; else if ((fp = fopen (name, "r")) == NULL) { fprintf (stderr, "Cannot read %s\n", name); return; } while (fgets (inp, sizeof inp, fp) != NULL) { register unsigned char *s, *p; int len = strlen (inp); if (len && inp[len - 1] == '\n') inp[--len] = '\0'; if (!*inp) { /* .sp N - propusk N pustyh strok */ space: fprintf (fout, ".sp 1\n"); continue; } /* obrezat' koncevye probely */ for(p = NULL, s = inp; *s; ++s){ if (!isspace (*s)) p = s; } if(p) p[1] = '\0'; else goto space; /* p ukazyvaet na poslednij neprobel */ /* Udalit' perenosy slov v konce stroki: perenos - eto minus, prizhatyj k koncu slova */ if (*p == '-' && p != inp /* ne v nachale stroki */ && isalnum(UC(p[-1])) /* posle bukvy */ ){ int c; *p = '\0'; /* zateret' perenos */ /* CHitaem prodolzhenie slova iz nachala sleduyushchej stroki */ while (isspace (c = getc (fp))); ungetc (c, fp); while ((c = getc (fp)) != '\n' && !isspace (c)) *p++ = c; *p = '\0'; if (c != '\n' ){ /* prochli probel */ /* vychityvaem VSE probely */ while (isspace(c = getc (fp))); if(c != '\n') ungetc (c, fp); } } /* .pp - direktiva nachala abzaca. */ if (isspace (*inp)) { fprintf (fout, ".pp\n"); for (s = inp; isspace (*s); s++); putstr (fout, s); } else { if (*inp == '.' || *inp == '\'') fprintf (fout, "\\&"); putstr (fout, inp); } } if( fp != stdin ) fclose (fp); } int main (int argc, char *argv[]) { int i; setlocale(LC_ALL, ""); for (i = 1; i < argc; i++) proceed (argv[i]); return 0; /* exit code */ } /* Primer 5 */ /* Programma, raspechatyvayushchaya slova v strokah fajla v obratnom poryadke */ #include <stdio.h> #include <ctype.h> #include <string.h> #include <locale.h> #define MAXL 255 /* maks. dlina stroki */ /* Esli by my ne vklyuchili ctype.h, to my dolzhny byli by opredelit' * #define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\f') */ main ( argc, argv ) char **argv;{ setlocale(LC_ALL, ""); if( argc == 1 ){ /* programma vyzvana bez argumentov */ munch( "" ); }else{ /* argumenty programmy - imena fajlov */ while( argv[ 1 ] ){ munch( argv[1] ); argv++; argc--; } } total(); exit(0); } /* obrabotat' fajl s imenem name */ munch( name ) char *name; { char l[MAXL]; /* bufer dlya ocherednoj stroki */ int len; /* dlina etoj stroki */ char *words[50]; /* tablica polej stroki */ char **s; /* sluzhebnaya */ int nwords; /* chislo slov v stroke */ FILE *fp; if( name == NULL || !*name ) fp = stdin; /* standartnyj vvod */ else if( (fp = fopen( name, "r" )) == NULL ){ fprintf( stderr, "Ne mogu otkryt' fajl %s\n", name ); return; } printf( "----------------------------%s----\n", name ); while( fgets( l, MAXL, fp ) != NULL ){ len = strlen( l ); if( len && l[len-1] == '\n' ) l[--len] = '\0' ; if( nwords = parse( l, words)){ /* raspechatka slov v obratnom poryadke */ for( --nwords; nwords >= 0; nwords-- ){ printf( "%s ", words[ nwords] ); add( words[ nwords ] ); } } putchar ('\n'); } if( fp != stdin ) fclose( fp ); } /* razobrat' stroku na slova */ parse( s, tabl ) register unsigned char *s; unsigned char *tabl[]; { char eol = 0; int nwords = 0; while ( !eol ){ /* propustit' probely i tabulyacii */ while(isspace(*s)) s++; if( !*s ) /* stroka konchilas' */ break; *tabl++ = s; nwords++; /* nachalo ocherednogo slova */ /* poka ne probel i ne konec stroki */ while( *s && !isspace(*s))s++; /* ukazatel' stoit na simvole, sleduyushchem za slovom */ if( ! *s ) eol ++; *s = '\0'; /* zakryli Slovo, nachinaem Delo */ s++; } *tabl = NULL; return nwords; } /* postroenie tablicy slov, vstrechayushchihsya v fajle */ #define MAXWORDS 1024 struct W{ int ctr; /* chislo vhozhdenij slova */ char *wrd; /* slovo */ }w [MAXWORDS]; /* tablica */ int busy = 0 ; /* zanyato v tablice */ extern char *malloc(); /* Dobavit' slovo v tablicu */ add( word ) char *word; { register i; static alert = 1; /* net li uzhe slova v tablice ? */ /* esli est' - prosto uvelichit' schetchik */ for( i = 0; i < busy ; i++ ){ if( !strcmp( word, w[i].wrd )){ w[i].ctr++; return; } } if( busy >= MAXWORDS ){ if( alert ){ fprintf( stderr, "Perepolnenie tablicy slov\7\n"); alert = 0; } return; } /* net, slova net. Zanosim: */ w[busy].wrd = malloc( strlen( word ) + 1 ); /* 1 bajt pod simvol \0 */ if( w[busy].wrd == NULL ){ fprintf( stderr, "Malo pamyati\n"); busy = MAXWORDS+1; /* yakoby perepolnenie */ return; } w[busy].ctr = 1; strcpy( w[busy].wrd, word ); busy++; } compare( a, b ) struct W *a, *b; { return strcoll( a-> wrd, b-> wrd ); /* strcoll sravnivaet slova v alfavitnom poryadke */ } /* vydacha vseh slov, vstrechennyh v tekste, i chisla ih vhozhdenij */ total(){ register i; /* sortiruem slova po alfavitu */ qsort( w, busy, sizeof(struct W), compare ); printf( "-----|-----------ITOG---------------\n"); for( i=0; i < busy; i++ ) printf( "%4d | %s\n", w[i].ctr, w[i].wrd ); } /* Primer 6 */ /* Sortirovka bukv v stroke metodom "puzyr'ka" (bubble sort) */ #define YES 1 #define NO 0 bsort(s) char *s; { register i; /* indeks sravnivaemoj bukvy */ register need = YES; /* nado li prodolzhat' sortirovku ? */ while( need ){ need = NO; /* ne nado */ for(i=0; s[i+1]; i++ ) /* uslovie cikla: my sravnivaem i-uyu i i+1-uyu bukvy, * poetomu i proveryaem nalichie i+1oj bukvy */ if( s[i] > s[i+1] ){ /* v nevernom poryadke */ swap( &s[i], &s[i+1] ); /* perestavit' */ need = YES; /* chto-to izmenilos': nado budet * povtorit' prosmotr massiva bukv */ } } } /* A vot variant sortirovki, napisannyj s ukazatelyami */ bpsort(s) char *s; { register char *p; register need = YES; while( need ){ need = NO; for( p = s; p[1] != '\0' ; p++ ) if( *p > *(p+1) ){ swap( p, p+1 ); need = YES; } } } /* obmen dvuh bukv, nahodyashchihsya po adresam s1 i s2 */ swap( s1, s2 ) register char *s1, *s2; { char tmp; /* temporary */ tmp = *s1; *s1 = *s2; *s2 = tmp; } char sample1[] = "Homo homini lupus est - ergo bibamus!"; char sample2[ sizeof sample1 ]; /* massiv takogo zhe razmera */ main(){ strcpy( sample2, sample1 ); /* skopirovat' */ bsort ( sample1 ); printf( "%s\n", sample1 ); bpsort( sample2 ); printf( "%s\n", sample2 ); } /* Primer 7 */ /* Rabota s hesh-tablicej. CHast' funkcij napisana tak, chtoby * byt' nezavisimoj ot tipov klyucha i znacheniya i legko * podvergat'sya modifikacii. */ #include <stdio.h> #include <string.h> /* prototype for strchr() */ extern void *malloc(unsigned size); /* tipy klyucha i znacheniya: v nashem sluchae eto stroki */ typedef unsigned char uchar; typedef uchar *VAL; typedef uchar *KEY; /* Dlya ispol'zovaniya sleduet realizovat' operacii int HASHFUNC(KEY); int EQKEY(KEY, KEY); void FREEVAL(VAL); void SETVAL(VAL, VAL); void FREEKEY(KEY); void SETKEY(KEY, KEY); */ #define HASHSIZE 21 /* razmer tablicy: ochen' horosho 2**n */ uchar *strudup(const uchar *s){ /* sozdanie kopii stroki v "kuche" */ uchar *p = (uchar *) malloc(strlen(s)+1); strcpy(p, s); return p; } /* odna iz vozmozhnyh hesh-funkcij */ unsigned int hash; /* poslednee vychislennoe znachenie hesh-funkcii */ int HASHFUNC(KEY key){ unsigned int i = 0; uchar *keysrc = key; while(*key){ i = (i << 1)|(i >> 15); /* ROL */ i ^= *key++; } hash = i % HASHSIZE; printf( "hash(%s)=%d\n", keysrc, hash); /* otladka */ return hash; } #define EQKEY(s1, s2) (strcmp(s1, s2) == 0) #define FREEKEY(s) free(s) #define FREEVAL(s) free(s) #define SETVAL(at,s) at = strudup(s) #define SETKEY(at,s) at = strudup(s) #define KEYFMT "%s" #define VALFMT "%s" /* ================== tipo-nezavisimaya chast' ================= */ struct cell { struct cell *next; /* ssylka na ocherednoj element */ KEY key; /* klyuch */ VAL val; /* znachenie */ } *hashtable[ HASHSIZE ]; /* hesh-tablica */ /* poluchenie znacheniya po klyuchu */ struct cell *get(KEY key){ struct cell *p; for(p = hashtable[HASHFUNC(key)]; p; p = p->next) if(EQKEY(p->key, key)) return p; return NULL; /* otsutstvuet */ } /* zanesti paru klyuch:znachenie v tablicu */ void set(KEY key, VAL val){ struct cell *p; /* proverit' - ne bylo li zvena s takim klyuchom */ if((p = get(key)) == NULL){ /* ne bylo */ if(!(p = (struct cell *) malloc(sizeof(*p)))) return; SETKEY(p->key, key); p->next = hashtable[hash]; /* hash vychisleno v get() */ hashtable[hash] = p; } else /* uzhe bylo: izmenit' znachenie */ FREEVAL(p->val); SETVAL(p->val, val); } /* udalenie po klyuchu */ int del(KEY key){ int indx = HASHFUNC(key); struct cell *p, *prev = NULL; if((p = hashtable[indx]) == NULL) return 0; for( ;p ;prev = p, p=p->next) if(EQKEY(p->key, key)){ FREEVAL(p->val); FREEKEY(p->key); if( p == hashtable[indx] ) /* golova spiska */ hashtable[indx] = p->next; else prev->next = p->next; free((void *) p ); return 1; /* udalen */ } return 0; /* ne bylo takogo */ } /* raspechatat' paru klyuch:znachenie */ void printcell(struct cell *ptr){ putchar('('); printf( KEYFMT, ptr->key ); putchar(','); printf( VALFMT, ptr->val ); putchar(')'); } /* raspechatka tablicy (dlya otladki) */ void printtable(){ register i; struct cell *p; printf("----TABLE CONTENTS----\n"); for(i=0; i < HASHSIZE; i++) if((p = hashtable[i]) != NULL){ printf( "%d: ", i); for(; p; p=p->next) printcell(p), putchar(' '); putchar('\n'); } } /* iterator */ struct celliter { int index; struct cell *ptr; }; /* vydat' ocherednoe znachenie */ struct cell *nextpair(struct celliter *ci){ struct cell *result; while((result = ci->ptr) == NULL){ if( ++(ci->index) >= HASHSIZE ) return NULL; /* bol'she net */ ci->ptr = hashtable[ci->index]; } ci->ptr = result->next; return result; } /* inicializaciya iteratora */ struct cell *resetiter(struct celliter *ci){ ci->index = (-1); ci->ptr = NULL; return nextpair(ci); /* pervoe znachenie */ } /* =========================================================== */ void main(){ /* tablica iz imen i razmerov fajlov tekushchego kataloga */ struct celliter ci; struct cell *cl; char key[40], value[40]; struct cell *val; extern FILE *popen(); FILE *fp; char *s ; /* popen() chitaet vyvod komandy, zadannoj v 1-om argumente */ fp = popen( "ls -s", "r" ); while( fscanf( fp, "%s%s", value, key) == 2 ) set(key, value); pclose(fp); /* popen() nado zakryvat' pclose(); */ for(;;){ printf( "-> " ); /* priglashenie */ if( !gets( key )) break; /* EOF */ if( *key == '-' ){ /* -KLYUCH :udalit' */ printf( del( key+1 ) ? "OK\n" : "net takogo\n"); continue; } if( !*key || !strcmp(key, "=")){ /* = :raspechatat' tablicu*/ printtable(); continue; } if(s = strchr(key, '=')){ /* KLYUCH=ZNACHENIE :dobavit' */ *s++ = '\0'; set(key, s); continue; } if((val = get( key )) == NULL) /* KLYUCH :najti znachenie */ printf( "net takogo klyucha\n"); else{ printf( "znachenie "); printf(VALFMT, val->val); putchar('\n'); } } /* raspechatka tablicy pri pomoshchi iteratora */ for( cl = resetiter(&ci) ; cl ; cl = nextpair(&ci)) printcell(cl), putchar('\n'); } /* Primer 8 */ /* Primer malen'koj bazy dannyh. * Dannye hranyatsya BEZ dublikatov. * Nado zametit', chto ispol'zuetsya plohoj (neeffektivnyj) * algoritm dostupa - linejnyj poisk. */ #include <stdio.h> /* Vse zapisi v baze imeyut fiksirovannyj razmer */ #define VLEN 20 #define KEY_FREE (-13) /* klyuch svobodnogo mesta. On vybran proizvol'no, no ne dolzhen vstrechat'sya v kachestve vhodnyh dannyh */ struct data{ short b_key; /* klyuch */ char b_val[VLEN]; /* stroka-znachenie */ }; char BASEF[] = ".base" ; /* imya fajla bazy */ FILE *fbase; /* pointer na bazu */ struct data tmp; /* vspomogatel'naya peremennaya */ void initBase (void){ /* fopen: r read (chtenie) * w write (zapis'), fajl peresozdaetsya. * (sozdaetsya, esli ne bylo, esli byl - opustoshaetsya). * r+ chtenie i zapis' (fajl uzhe sushchestvuet). * w+ chtenie i zapis' (sozdaetsya pustoj fajl). * a append (zapis' v konec fajla), sozdat' esli net: * imeetsya v vidu, chto KAZHDAYA operaciya zapisi snachala * stavit ukazatel' zapisi na konec fajla. * V MS DOS netekstovyj fajl NEOBHODIMO otkryvat' kak * rb wb rb+ wb+ ab+ inache nichego ne budet rabotat'. */ if(( fbase = fopen( BASEF, "r+" )) == NULL ){ if(( fbase = fopen( BASEF, "w+" )) == NULL ){ fprintf( stderr, "Ne mogu otkryt' bazu dannyh %s\n", BASEF ); exit(1); } fprintf( stderr, "Baza sozdana\n" ); } } void closeBase (void){ fclose( fbase ); } /* Uchtite, chto esli vy zapisyvaete v fajl struktury, to v fajle ne budet razdeleniya na stroki - fajl NETEKSTOVYJ! Poetomu i chitat' takoj fajl mozhno tol'ko strukturami: read(), fread() (no ne scanf-om i ne fgets-om) */ /* Poisk po klyuchu . Vydat' (-1), esli zapisi s dannym klyuchom net, inache - nomer slota, gde soderzhitsya zapis' s dannym klyuchom. */ int bget (int key) { int n; /* posledovatel'no prosmotret' ves' fajl */ rewind( fbase ); /* v nachalo fajla. Ravno fseek(fbase, 0L, 0); */ n = 0 ; /* int skol'ko_elementov_massiva_dejstvitel'no_schitano = * fread( adres_massiva_kuda_schityvat', * razmer_odnogo_elementa_massiva, * skol'ko_elementov_schityvat'_v_massiv, kanal ); * Zamet'te, chto kolichestvo dannyh zadaetsya NE v bajtah, * a v 'shtukah' */ while( fread( &tmp, sizeof( tmp ), 1, fbase ) == 1 ){ if( tmp.b_key == key ) return n; n++; } return (-1); /* ne najdeno */ } /* modificirovat' zapis' s indeksom ind */ void bmod ( int ind, int key, /* novyj klyuch */ char *val /* novoe znachenie */ ) { struct data new; fseek( fbase, (long) sizeof( struct data ) * ind, 0 ); new.b_key = key; strncpy( new.b_val, val, VLEN ); /* int skol'ko_elementov_massiva_dejstvitel'no_zapisano = * fwrite( adres_massiva_kotoryj_zapisyvat', * razmer_odnogo_elementa_massiva, * skol'ko_elementov_massiva_zapisyvat', kanal ); */ if( fwrite( &new, sizeof new , 1, fbase ) != 1 ) fprintf( stderr, "Oshibka zapisi.\n" ); } /* udalenie zapisi po klyuchu */ int bdel (int key){ int ind = bget( key ); if( ind == -1 ) return (-1); /* zapisi s takim klyuchom net */ bmod( ind, KEY_FREE, "" ); /* zapisat' priznak svobodnogo mesta */ return 0; } /* Sluzhebnaya procedura dopisi k koncu fajla */ void bappend (int key, char *val) { struct data new; /* vstat' na konec fajla */ fseek( fbase, 0L, 2 ); /* i zapisat' novuyu strukturu v konec */ new.b_key = key; strncpy( new.b_val, val, VLEN ); fwrite( &new, sizeof( struct data ) , 1, fbase ); } /* dobavlenie novoj zapisi. Esli zapis' s takim klyuchom uzhe est' - vydat' oshibku */ int bput (int key, char *val) { int i = bget( key ); if( i != -1 ) return (-1); /* zapis' uzhe est' */ /* najti svobodnoe mesto */ i = bget( KEY_FREE ); if( i == -1 ) { /* net svobodnyh mest */ bappend( key, val ); return 0; } /* inache svobodnoe mesto najdeno. * Zamenyaem dyrku na poleznuyu informaciyu */ bmod( i, key, val ); } /* raspechatat' vsyu bazu dannyh podryad */ void bprint (void){ int n; int here = 0; rewind( fbase ); n = 0; printf( "-nomer--klyuch-------znachenie-----------------\n" ); while( fread( &tmp, sizeof tmp, 1, fbase ) == 1 ){ if( tmp.b_key == KEY_FREE ){ n++; continue; } printf( "#%-2d| %6d\t| %s\n", n, tmp.b_key, tmp.b_val ); here ++; n++; } printf( "--------------------------------------------\n" ); printf( "Dlina bazy:%d Zanyato:%d\n\n", n, here ); } /* zamena polya val u zapisi s klyuchom key */ int bchange (int key, char *val) { int ind; ind = bget( key ); if( ind == -1 ){ /* zapis' s takim klyuchom ne sushchestvuet */ /* Dobavit' kak novuyu zapis' */ bput( key, val ); return 0; } bmod( ind, key, val ); return 1; } /* Analogichnaya funkciya, no ispol'zuyushchaya drugoj sposob. * Krome togo, esli takoj klyuch otsutstvuet - nichego ne delaetsya */ int bchg (int key, char *val) { struct data d; rewind( fbase ); /* v nachalo fajla */ while( fread( &d, sizeof d, 1, fbase ) == 1 ){ /* poisk klyucha */ if( d.b_key == key ){ /* vernut'sya nazad ot tekushchej pozicii */ fseek( fbase, - (long) sizeof d, 1 ); /* ne goditsya (long)-sizeof d !!! */ d.b_key = key; strncpy( d.b_val, val, VLEN ); fwrite( &d, sizeof d, 1, fbase ); /* mezhdu fread i fwrite dolzhen byt' * hot' odin fseek. (magicheskoe zaklinanie!) */ fseek( fbase, 0L, 1); /* nikuda ne sdvigat'sya */ return 0; /* sdelano */ } } return (-1); /* takogo klyucha ne bylo */ } /* Primer */ void main (void){ int i; initBase(); bprint(); bdel( 8 ); printf( "Sozdaem bazu dannyh\n" ); bput( 1, "stroka 1" ); bput( 2, "stroka 2" ); bput( 3, "stroka 3" ); bput( 4, "stroka 4" ); bprint(); printf( "Udalyaem zapisi s klyuchami 1 i 3\n" ); bdel( 1 ); bdel( 3 ); bprint(); printf( "Dobavlyaem zapisi 5, 6 i 7\n" ); bput( 5, "stroka 5" ); bput( 6, "stroka 6" ); bput( 7, "stroka 7" ); bprint(); printf( "Zamenyaem stroku v zapisi s klyuchom 2\n" ); bchange( 2, "novaya stroka 2" ); bprint(); printf( "Zamenyaem stroku v zapisi s klyuchom 4\n" ); bchg( 4, "novaya stroka 4" ); bprint(); printf( "Zamenyaem stroku v zapisi s klyuchom 6 i klyuch 6 na 8\n" ); i = bget( 6 ); printf( "Sejchas zapis' s klyuchom 6 soderzhit \"%s\"\n", tmp.b_val ); bmod( i, 8, "Novaya stroka 6/8" ); bprint(); closeBase(); } /* Primer 9 */ /* Vstavka/udalenie strok v fajl */ #include <stdio.h> #define INSERT_BEFORE 1 /* Vstavit' stroku pered ukazannoj */ #define INSERT_AFTER 2 /* Vstavit' stroku posle ukazannoj */ #define DELETE 3 /* Udalit' stroku */ #define REPLACE 4 /* Zamenit' stroku */ /* K kazhdoj stroke linenum dolzhno otnosit'sya ne bolee 1 operacii !!! */ struct lineop { char op; /* Operaciya */ long linenum; /* Nomer stroki v fajle (s 0) */ char *str; /* Stroka (ili NULL dlya DELETE) */ }; long lineno; /* nomer tekushchej stroki */ int fileChange (char *name, /* imya fajla */ struct lineop ops[], /* zadanie */ int nops /* chislo elementov v massive ops[] */ ){ FILE *fin, *fout; static char TMPNAME[] = " ? "; char buffer[BUFSIZ]; register i; struct lineop tmpop; if ((fin = fopen (name, "r")) == NULL) return (-1); if ((fout = fopen (TMPNAME, "w")) == NULL) { fclose (fin); return (-1); } lineno = 0L; while (fgets (buffer, BUFSIZ, fin) != NULL) { if( nops ) for (i = 0; i < nops; i++) if (lineno == ops[i].linenum) { switch (ops[i].op) { case DELETE: /* udalit' */ break; case INSERT_BEFORE: /* vstavit' pered */ fprintf (fout, "%s\n", ops[i].str); fputs (buffer, fout); break; case INSERT_AFTER: /* vstavit' posle */ fputs (buffer, fout); fprintf (fout, "%s\n", ops[i].str); break; case REPLACE: /* zamenit' */ fprintf (fout, "%s\n", ops[i].str); break; } /* perestavit' vypolnennuyu operaciyu v konec massiva i zabyt' */ tmpop = ops[nops-1]; ops[nops-1] = ops[i]; ops[i] = tmpop; nops--; goto next; } /* inache stroka ne chislitsya v massive ops[] : skopirovat' */ fputs (buffer, fout); next: lineno++; } fclose (fin); fclose (fout); rename (TMPNAME, name); return nops; /* chislo nesdelannyh operacij (0 - vse sdelano) */ } struct lineop myops[] = { { DELETE, 2L, NULL }, { INSERT_BEFORE, 0L, "inserted before 0" }, { INSERT_BEFORE, 10L, "inserted before 10" }, { INSERT_AFTER, 5L, "inserted after 5" }, { DELETE, 6L, NULL }, { INSERT_AFTER, 8L, "inserted after 8" }, { INSERT_AFTER, 12L, "inserted after 12" }, { REPLACE, 3L, "3 replaced" } }; void main( void ){ int n; n = fileChange( "aFile", myops, sizeof(myops)/sizeof(struct lineop)); printf( "Strok v fajle: %ld; ostalos' operacij: %d\n", lineno, n); } /* ishodnyj fajl poluchivshijsya fajl line 0 inserted before 0 line 1 line 0 line 2 line 1 line 3 3 replaced line 4 line 4 line 5 line 5 line 6 inserted after 5 line 7 line 7 line 8 line 8 line 9 inserted after 8 line 10 line 9 inserted before 10 line 10 Strok v fajle: 11; ostalos' operacij: 1 */ /* Primer 10 */ /* Problema: pozvolit' delat' vyzov free(ptr) * na dannye, ne otvodivshiesya malloc()-om. * Reshenie: vesti spisok vseh dannyh, * otvedennyh malloc()om. * Vozmozhno takzhe otslezhivanie diapazona adresov, * no poslednee yavlyaetsya mashinno-zavisimym resheniem. * * Pri bol'shom kolichestve fajlov eta programma - neplohoj test * proizvoditel'nosti mashiny! */ #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct _cell { void *addr; struct _cell *next; } Cell; typedef struct _entry { int length; int used; Cell *list; } Entry; /* Heshirovannaya tablica */ #define NENTRIES 64 Entry aTab[NENTRIES]; /* Hesh-funkciya ot adresa */ int aHash(void *addr){ unsigned long x = (unsigned long) addr; x >>= 3; /* delenie na 8, tak kak adresa iz malloc() obychno chetnye, poskol'ku vyrovneny na granicu double */ return(x % NENTRIES); /* Tut k mestu napomnit', chto vychislenie ostatka ot deleniya na stepen' dvojki * mozhno soptimizirovat': * x % (2**N) = x & 0b0001.....1 (N dvoichnyh edinic) * K primeru, x % 64 = x & 0x3F; (6-aya stepen' dvojki) */ } /* Vydelit' pamyat', zapisat' adres v tablicu */ void *aCalloc(int n, int m){ void *ptr = calloc(n, m); Entry *ep = &aTab[ aHash(ptr) ]; Cell *p; for(p=ep->list; p; p=p->next) if(p->addr == NULL){ /* Svobodnaya yachejka: pereispol'zovat' */ p->addr = ptr; ep->used++; return ptr; } /* Net svobodnyh, zavesti novuyu */ p = (Cell *) calloc(1, sizeof(Cell)); p->addr = ptr; p->next = ep->list; ep->list = p; ep->length++; ep->used++; return ptr; } /* Osvobodit' pamyat' */ int aFree(void *ptr){ Entry *ep = &aTab[ aHash(ptr) ]; Cell *p; for(p=ep->list; p; p=p->next) if(p->addr == ptr){ free(ptr); p->addr = NULL; /* YAchejka ne udalyaetsya, no metitsya kak svobodnaya */ ep->used--; return 1; } /* Net, takoj ukazatel' ne otvodilsya. * Ne delat' free() */ return 0; } /* Vydat' statistiku ob ispol'zovanii hesha */ void aStat(){ int i; int len_all; int used_all; for(i=len_all=used_all=0; i < NENTRIES; i++){ len_all += aTab[i].length; used_all += aTab[i].used; printf("%d/%d%s", aTab[i].used, aTab[i].length, i==NENTRIES-1 ? "\n":" "); } printf("%d/%d=%g%%\n", used_all, len_all, (double)used_all * 100 / len_all); } /* TEST =================================================================*/ Cell *text; /* Prochitat' fajl v pamyat' */ void fileIn(char *name){ char buf[10000]; FILE *fp; if((fp = fopen(name, "r")) == NULL){ printf("Cannot read %s\n", name); return; } while(fgets(buf, sizeof buf, fp) != NULL){ char *s; Cell *p; s = (char *) aCalloc(1, strlen(buf)+1); strcpy(s, buf); p = (Cell *) aCalloc(sizeof(Cell), 1); p->addr = s; p->next = text; text = p; } fclose(fp); } /* Unichtozhit' tekst v pamyati */ void killAll(){ Cell *ptr, *nxtp; ptr = text; while(ptr){ nxtp = ptr->next; if(!aFree(ptr->addr)) printf("No free(1)\n"); if(!aFree(ptr)) printf("No free(2)\n"); ptr = nxtp; } } /* Udalit' iz teksta stroki, nachinayushchiesya s opredelennoj bukvy */ void randomKill(int *deleted){ unsigned char c = rand() % 256; Cell *ptr, *prevp; unsigned char *s; retry: prevp = NULL; ptr = text; while(ptr){ s = (unsigned char *) ptr->addr; if(*s == c){ /* nashel */ if(!aFree(s)) printf("No free(3)\n"); /* isklyuchit' iz spiska */ if(prevp) prevp->next = ptr->next; else text = ptr->next; if(!aFree(ptr)) printf("No free(4)\n"); /* Zavedomo nepravil'nyj free if(!aFree(ptr+1)) printf("No free(5)\n"); */ (*deleted)++; goto retry; } prevp = ptr; ptr = ptr->next; } } int main(int ac, char *av[]){ int i, r, d; char buffer[4098]; srand(time(NULL)); for(i=1; i < ac; i++){ printf("File: %s\n", av[i]); fileIn(av[i]); aStat(); d = 0; for(r=0; r < 128; r++) randomKill(&d); printf("%d lines deleted\n", d); aStat(); } killAll(); aStat(); if(!aFree(buffer)) printf("buffer[] - ne dinamicheskaya peremennaya.\n"); return 0; } /* Primer 11 */ /* Paket dlya lovli naezdov oblastej vydelennoj pamyati * drug na druga, * a takzhe prosto povrezhdenij dinamicheski otvedennoj pamyati. */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> /* O_RDWR */ #include <sys/types.h> #include <ctype.h> #include <locale.h> #define CHECKALL /* ----------------- <--------- ptr | red_zone | golovnaya "pogranichnaya zona" ----------------- | byte[0] | | ... | | byte[size-1] | | placeholder | ----------------- vyrovneno na granicu RedZoneType | red_zone | hvostovaya "pogranichnaya zona" ----------------- Osnovnye idei sostoyat v sleduyushchem: 1) Pered i posle oblasti dannyh stroitsya zona, zapolnennaya zaranee izvestnym "uzorom". Esli ee soderzhimoe izmenilos', isporcheno - znachit my gde-to razrushili nashu pamyat'. 2) Vedetsya tablica vseh otvedennyh malloc()-om segmentov pamyati; dlya ekonomii mesta eta tablica vynesena v fajl (no zato eto ochen' medlenno). 3) My ne mozhem pol'zovat'sya bibliotekoj STDIO dlya obmenov s fajlom, potomu chto eta biblioteka sama ispol'zuet malloc() i bufera mogut byt' razrusheny. */ typedef char *RedZoneType; /* vyravnivanie na granicu ukazatelya */ /* Mozhno vyravnivat' na granicu double: typedef double RedZoneType; */ /* Segment, vydelyaemyj v operativnoj pamyati */ typedef struct _allocFrame { RedZoneType red_zone; /* golovnaya "pogranichnaya zona" */ RedZoneType stuff[1]; /* mesto dlya dannyh */ /* hvostovaya "pogranichnaya zona" bezymyanna */ } AllocFrame; const int RedZoneTypeSize = sizeof(RedZoneType); /* Zapis', pomeshchaemaya v tablicu vseh vydelennyh malloc()om * oblastej pamyati. */ typedef struct _memFileRecord { AllocFrame *ptr; /* adres */ size_t size, adjsize; /* razmer vydelennoj oblasti */ /* (0,0) - oznachaet "segment osvobozhden" */ int serial; } MemFileRecord; char red_table[] = { 0x01, 0x03, 0x02, 0x04, 0x11, 0x13, 0x12, 0x14, 0x21, 0x23, 0x22, 0x24, 0x31, 0x33, 0x32, 0x34 }; char free_table[] = { 'F', 'r', 'e', 'e', 'p', 't', 'r', '\0', 'F', 'r', 'e', 'e', 'p', 't', 'r', '\0' }; /* Fajl dlya hraneniya tablicy ukazatelej */ static int mem_fd = (-1); #define PTABLE "PointerTable.bin" #define NRECORDS 256 MemFileRecord memrecords[NRECORDS]; /* ============================================================= */ void MEMputTableRecord(AllocFrame *newptr, AllocFrame *oldptr, size_t size, size_t adjsize); void MEMputTableRecordKilled(AllocFrame *ptr); void MEMerasePreviousRecords(AllocFrame *ptr); int MEMcheckRecord(MemFileRecord *rec); int MEMcheck_consistency(AllocFrame *ptr); void MEMmakeRedZones(char *cptr, size_t size, size_t adjsize); void MEMopenFd(); /* ============================================================= */ /* |tim sleduet pol'zovat'sya vmesto standartnyh funkcij */ void *MEMmalloc (size_t size); void *MEMrealloc(void *ptr, size_t size); void *MEMcalloc (size_t n, size_t size); void MEMfree (void *ptr); void MEMcheckAll(); /* eto mozhno vyzyvat' v seredine programmy */ /* ============================================================= */ void MEMopenFd(){ if(mem_fd < 0){ close(creat(PTABLE, 0644)); /* sozdat' fajl */ mem_fd = open(PTABLE, O_RDWR); /* chtenie+zapis' */ unlink(PTABLE); /* tol'ko dlya M_UNIX */ atexit(MEMcheckAll); setlocale(LC_ALL, ""); } } /* Pomestit' zapis' v tablicu vseh ukazatelej na * vydelennye oblasti pamyati. */ void MEMputTableRecord(AllocFrame *newptr, /* dlya zapisi */ AllocFrame *oldptr, /* dlya stiraniya */ size_t size, /* razmer dannyh */ size_t adjsize /* razmer vsej zapisi s zonami */ ){ MemFileRecord memrecord; static int serial = 0; memrecord.ptr = newptr; memrecord.size = size; memrecord.adjsize = adjsize; memrecord.serial = serial++; MEMopenFd(); #ifdef CHECKALL /* steret' prezhnie zapisi pro etot adres */ MEMerasePreviousRecords(oldptr); #endif lseek(mem_fd, 0L, SEEK_END); /* v konec */ write(mem_fd, &memrecord, sizeof memrecord); /* dobavit' */ } /* Sdelat' zapis' ob unichtozhenii oblasti pamyati */ void MEMputTableRecordKilled(AllocFrame *ptr){ /* Pometit' kak size=0, adjsize=0 */ MEMputTableRecord(ptr, ptr, 0, 0); } /* Kody otveta funkcii proverki */ #define OK 0 /* vse horosho */ #define DAMAGED 1 /* povrezhdena "pogranzona" */ #define FREED 2 /* eta pamyat' uzhe osvobozhdena */ #define NOTHERE (-1) /* net v tablice */ /* Proverit' sohrannost' "pogranichnyh zon" */ int MEMcheckRecord(MemFileRecord *rec){ int code = OK; char *cptr; register i; AllocFrame *ptr = rec->ptr; size_t size = rec->size; size_t adjsize = rec->adjsize; if(size == 0 && adjsize == 0){ printf("%p [%p] -- segment uzhe osvobozhden, " "record=#%d.\n", &ptr->stuff[0], ptr, rec->serial ); return FREED; } cptr = (char *) ptr; for(i=0; i < adjsize; i++){ if(i < RedZoneTypeSize || i >= RedZoneTypeSize + size ){ /* golovnaya pogranzona ILI hvostovaya pogranzona */ if( cptr[i] != red_table[ i % RedZoneTypeSize ] ){ printf("%p [%p] -- isporchen bajt %4d [%4d]" "= 0x%02X '%c' record=#%d size=%lu.\n", &ptr->stuff[0], ptr, i - RedZoneTypeSize, i, cptr[i] & 0xFF, isprint(cptr[i] & 0xFF) ? cptr[i] & 0xFF : '?', rec->serial, size ); code = DAMAGED; } } } for(i=0; i < RedZoneTypeSize; i++) if(cptr[i] == free_table[i]){ printf("%p -- uzhe osvobozhdeno?\n", ptr); code = FREED; } if(code != OK) putchar('\n'); return code; } /* Proverit' sohrannost' pamyati po ukazatelyu ptr. */ int MEMcheck_consistency(AllocFrame *ptr){ MemFileRecord mr_found; int nrecords, i, found = 0; size_t size; MEMopenFd(); /* Ishchem zapis' v tablice ukazatelej */ lseek(mem_fd, 0L, SEEK_SET); /* peremotat' v nachalo */ for(;;){ size = read(mem_fd, memrecords, sizeof memrecords); nrecords = size / sizeof(memrecords[0]); if(nrecords <= 0) break; for(i=0; i < nrecords; i++) if(memrecords[i].ptr == ptr){ /* My ishchem poslednyuyu zapis' pro pamyat' * s takim adresom, poetomu * vynuzhdeny prochitat' VESX fajl. */ mr_found = memrecords[i]; found++; } } if(found) { return MEMcheckRecord(&mr_found); } else { printf("%p -- zapis' v tablice otsutstvuet.\n", ptr); return NOTHERE; } } /* Unichtozhit' vse prezhnie zapisi pro ptr, propisyvaya ih adjsize=0 */ void MEMerasePreviousRecords(AllocFrame *ptr){ int nrecords, i, found; size_t size; MEMopenFd(); lseek(mem_fd, 0L, SEEK_SET); /* peremotat' v nachalo */ for(;;){ found = 0; size = read(mem_fd, memrecords, sizeof memrecords); nrecords = size / sizeof(memrecords[0]); if(nrecords <= 0) break; for(i=0; i < nrecords; i++) if(memrecords[i].ptr == ptr){ memrecords[i].adjsize = 0; /* memrecords[i].size = 0; */ found++; } if(found){ lseek(mem_fd, -size, SEEK_CUR); /* shag nazad */ write(mem_fd, memrecords, size); /* perezapisat' */ } } } void MEMcheckAll(){ #ifdef CHECKALL int nrecords, i; size_t size; printf("Proverka vseh ukazatelej -------------\n"); MEMopenFd(); lseek(mem_fd, 0L, SEEK_SET); /* peremotat' v nachalo */ for(;;){ size = read(mem_fd, memrecords, sizeof memrecords); nrecords = size / sizeof(memrecords[0]); if(nrecords <= 0) break; for(i=0; i < nrecords; i++) if(memrecords[i].adjsize != 0) MEMcheckRecord(&memrecords[i]); } printf("Proverka vseh ukazatelej zavershena ---\n"); #endif } /* ============================================================= */ /* Zapolnenie pogranichnyh zon obrazcom - "sledovoj dorozhkoj" */ void MEMmakeRedZones(char *cptr, size_t size, size_t adjsize){ register i; for(i=0; i < adjsize; i++){ if(i < RedZoneTypeSize || i >= RedZoneTypeSize + size ){ /* golovnaya pogranzona ILI * hvostovaya pogranzona + dopolnenie * do celogo chisla RedZoneType-ov */ cptr[i] = red_table[ i % RedZoneTypeSize ]; } } } /* ============================================================= */ /* Funkciya vydeleniya pamyati */ void *MEMmalloc(size_t size){ AllocFrame *retptr; int fullRedZoneTypes = (size + RedZoneTypeSize - 1) / RedZoneTypeSize; size_t adjustedSize = sizeof(retptr->red_zone) * 2 + /* dve pogranzony */ fullRedZoneTypes * RedZoneTypeSize; retptr = (AllocFrame *) malloc(adjustedSize); if(retptr == NULL) return NULL; MEMmakeRedZones ((char *) retptr, size, adjustedSize); MEMputTableRecord(retptr, retptr, size, adjustedSize); return &retptr->stuff[0]; /* vernut' ukazatel' na zonu dannyh */ } void *MEMrealloc(void *ptr, size_t size){ AllocFrame *retptr; char *cptr = (char *)ptr - RedZoneTypeSize; /* prezhnij AllocFrame */ AllocFrame *oldptr = (AllocFrame *) cptr; int fullRedZoneTypes = (size + RedZoneTypeSize - 1) / RedZoneTypeSize; size_t adjustedSize = sizeof(retptr->red_zone) * 2 + fullRedZoneTypes * RedZoneTypeSize; /* Proverit' sohrannost' togo, chto my sejchas budem realloc-it' */ MEMcheck_consistency(oldptr); retptr = (AllocFrame *) realloc((void *)oldptr, adjustedSize); if(retptr == NULL) return NULL; MEMmakeRedZones ((char *) retptr, size, adjustedSize); MEMputTableRecord(retptr, oldptr, size, adjustedSize); return &retptr->stuff[0]; } void *MEMcalloc(size_t n, size_t size){ size_t newsize = n * size; void *ptr = MEMmalloc(newsize); memset(ptr, '\0', newsize); return ptr; } /* Ochistka otvedennoj pamyati. * ptr - eto ukazatel' ne na AllocFrame, * a na dannye - to est' na stuff[0]. */ void MEMfree(void *ptr){ char *cptr = (char *)ptr - RedZoneTypeSize; int i, code; code = MEMcheck_consistency((AllocFrame *) cptr); for(i=0; i < RedZoneTypeSize; i++) cptr[i] = free_table[i]; if(code != FREED) free((void *) cptr); MEMputTableRecordKilled((AllocFrame *) cptr); } /* ============================================================= */ /* Testovyj primer */ /* ============================================================= */ #define MAXPTRS 512 char *testtable[MAXPTRS]; /* Sgenerirovat' stroku sluchajnoj dliny so sluchajnym soderzhimym */ char *wildstring(int c){ #define N 1024 char teststring[N + 1]; int len, i; char *ptr; len = rand() % N; for(i=0; i < len; i++) teststring[i] = c; teststring[len] = '\0'; ptr = (char *) MEMmalloc(len + 1); if(ptr){ strcpy(ptr, teststring); } else printf("NULL wildstring()\n"); return ptr; } int main(int ac, char *av[]){ int ilen, len, n, i; srand(time(NULL)); for(n=0; n < MAXPTRS; n++) testtable[n] = wildstring('A'); #define DAMAGE (MAXPTRS/3*2-1) #ifdef DAMAGE /* Navesti porchu */ len = strlen(testtable[DAMAGE]); testtable[DAMAGE][len+1] = 'x'; testtable[DAMAGE][-2] = 'y'; printf("ptr=%p len=%d\n", testtable[DAMAGE], len); #endif for(n=0; n < MAXPTRS/2; n++){ char *p = wildstring('B'); int length = strlen(p); char *ptr; i = rand() % MAXPTRS; /* Ne zabyt' prisvoit' vozvrashchennoe realloc() znachenie * obratno v testtable[i] !!! */ testtable[i] = ptr = (char *) MEMrealloc(testtable[i], length + 1); if(ptr == NULL) printf("Ne mogu sdelat' realloc()\n"); else strcpy(ptr, p); #ifdef DAMAGE /* Porcha */ if(n == MAXPTRS/3){ ptr[length+2] = 'z'; } #endif MEMfree(p); } for(n=0; n < MAXPTRS; n++){ if(testtable[n]) MEMfree(testtable[n]); } #ifdef DAMAGE MEMfree(testtable[DAMAGE]); #endif return 0; } /* Primer 12 */ /* Programma, sovmeshchayushchaya komandy mv i cp. Illyustraciya raboty s fajlami. * Primer togo, kak programma mozhet vybirat' rod raboty * po svoemu nazvaniyu. * Kompilyaciya: * cc cpmv.c -o copy ; ln copy move * Po motivam knigi M.Dansmura i G.Dejvisa. */ #include <stdio.h> /* buferizovannyj vvod/vyvod */ #include <sys/types.h> /* sistemnye tipy dannyh */ #include <sys/stat.h> /* struct stat */ #include <fcntl.h> /* O_RDONLY */ #include <errno.h> /* sistemnye kody oshibok */ /* #define strrchr rindex /* dlya versii DEMOS (BSD) */ extern char *strrchr(char *, char); /* iz biblioteki libc.a */ extern int errno; char MV[] = "move"; char CP[] = "copy"; #define OK 1 /* success - uspeh */ #define FAILED 0 /* failure - neudacha */ #define YES OK #define NO 0 /* Vydelit' bazovoe imya fajla: * ../wawa/xxx --> xxx * zzz --> zzz * / --> / */ char *basename( char *name ){ char *s = strrchr( name , '/' ); return (s == NULL) ? name : /* net sleshej */ (s[1] == '\0') ? name : /* kornevoj katalog */ s + 1; } #define ECANTSTAT (-1) /* fajl ne sushchestvuet */ struct ftype { unsigned type; /* tip fajla */ dev_t dev; /* kod ustrojstva, soderzhashchego fajl */ ino_t ino; /* indeksnyj uzel fajla na etom ustrojstve */ }; /* Poluchenie tipa fajla */ struct ftype filetype( char *name /* imya fajla */ ) { struct stat st; struct ftype f; if( stat( name, &st ) < 0 ){ f.type = ECANTSTAT; f.dev = f.ino = 0; } else { f.type = st.st_mode & S_IFMT; f.dev = st.st_dev; f.ino = st.st_ino; } return f; } /* Udalyaet fajly, krome ustrojstv */ int unlinkd( char *name, unsigned type ) { if( type == S_IFBLK || type == S_IFCHR || type == S_IFDIR) return 0; return unlink( name ); } /* Funkciya nizhnego urovnya: kopirovanie informacii bol'shimi porciyami */ int copyfile( int from, int to ) /* from - deskriptor otkuda */ /* to - deskriptor kuda */ { char buffer[ BUFSIZ ]; int n; /* chislo prochitannyh bajt */ while(( n = read( from, buffer, BUFSIZ )) > 0 ) /* read vozvrashchaet chislo prochitannyh bajt, * 0 v konce fajla */ if( write( to, buffer, n ) != n ){ printf( "Write error.\n" ); return FAILED; } return OK; } /* Kopirovanie fajla */ int docopy(char *src, char *dst, unsigned typefrom, unsigned typeto) { int retc; int fdin, fdout; printf( "copy %s --> %s\n", src, dst ); if((fdin = open( src, O_RDONLY )) < 0 ){ printf( "San't read %s\n", src ); return FAILED; } if((fdout = creat( dst, 0644 )) < 0 ){ /* rw-r--r-- */ printf( "Can't create %s\n", dst ); return FAILED; } retc = copyfile( fdin, fdout ); close( fdin ); close( fdout ); return retc; } /* Pereimenovanie fajla. Vernut' OK, esli udachno, FAILED - neudachno */ int mlink(char *src, char *dst, unsigned typefrom, unsigned typeto) { switch( typefrom ){ case S_IFDIR: /* pereimenovanie kataloga */ printf( "rename directory %s --> %s\n", src, dst ); if( access( dst, 0 ) == 0 ){ /* 0 - proverit' sushchestvovanie fajla */ printf( "%s exists already\n", dst ); /* fajl uzhe sushchestvuet */ return FAILED; } if( link( src, dst ) < 0 ){ printf( "Can't link to directory %s\n", dst ); perror( "link" ); /* Vozmozhno, chto dlya vypolneniya link() dlya katalogov, * programma dolzhna obladat' pravami superpol'zovatelya. */ return FAILED; } unlink( src ); return OK; default: /* dst - ne sushchestvuet ili obychnyj fajl */ printf( "move %s --> %s\n", src, dst ); unlinkd( dst, typeto ); /* zachishchaem mesto, t.k. lin