et, rset; /* maski */ struct timeval timeout, rtimeout; FD_ZERO(&set); nopen = 0; /* ochistka maski */ FD_SET (PP.pfd, &set); nopen++; /* uchest' v maske */ FD_SET (STDIN, &set); nopen++; maxfd = max(PP.pfd, STDIN); timeout.tv_sec = 3600; /* sekund */ timeout.tv_usec = 0; /* millisekund */ A. Bogatyrev, 1992-95 - 270 - Si v UNIX nfds = maxfd + 1; while( nopen ){ rset = set; rtimeout = timeout; /* oprosit' deskriptory */ if((nready = select( nfds, &rset, NULL, NULL, &rtimeout )) <= 0) continue; for(f=0; f < nfds; f++ ) if( FD_ISSET(f, &rset)){ /* deskriptor f gotov */ int n; if((n = read(f, buf, sizeof buf)) <= 0 ){ FD_CLR(f, &set); nopen--; /* isklyuchit' */ close(f); } else { int fdout; /* uchet i kontrol' */ if( f == PP.pfd ){ fdout = STDOUT; PP.out_bytes += n; if( fpscript ) fwrite(buf, 1, n, fpscript); } else if( f == STDIN ) { fdout = PP.pfd; PP.in_bytes += n; if( halfflag && fpscript ) fwrite(buf, 1, n, fpscript); if( autoecho ) write(STDOUT, buf, n); } write(fdout, buf, n); } } } } A. Bogatyrev, 1992-95 - 271 - Si v UNIX int main(ac, av) char **av; { while( ac > 1 && *av[1] == '-' ){ switch(av[1][1]){ case 's': scriptflg++; break; case 'f': av++; ac--; protocol = av[1]; scriptflg++; break; case 'h': halfflag++; break; case 'a': autoecho++; break; default: fprintf(stderr, "Bad key %s\n", av[1]); break; } ac--; av++; } if( scriptflg ){ fpscript = fopen( protocol, "w" ); } ac--; av++; wm_init(); PP = wm_ptypair(); if( PP.pfd < 0 ){ fprintf(stderr, "Cannot get pty. Please wait and try again.\n"); return 1; } wm_fixtty(); wm_startshell(ac, av); go++; wm_select(); wm_done(0); /* NOTREACHED */ return 0; } 6.12. Prostoj interpretator komand. Dannyj razdel prosto privodit ishodnyj tekst prostogo interpretatora komand. Funkciya match opisana v glave "Tekstovaya obrabotka". A. Bogatyrev, 1992-95 - 272 - Si v UNIX /* Primitivnyj interpretator komand. Raspoznaet postrochno * komandy vida: CMD ARG1 ... ARGn <FILE >FILE >>FILE >&FILE >>&FILE * Sborka: cc -U42 -DCWDONLY sh.c match.c pwd.c -o sh */ #include <sys/types.h>/* opredelenie tipov, ispol'zuemyh sistemoj */ #include <stdio.h> /* opisanie biblioteki vvoda/vyvoda */ #include <signal.h> /* opisanie signalov */ #include <fcntl.h> /* opredelenie O_RDONLY */ #include <errno.h> /* kody sistemnyh oshibok */ #include <ctype.h> /* makrosy dlya raboty s simvolami */ #include <dirent.h> /* emulyaciya fajlovoj sistemy BSD 4.2 */ #include <pwd.h> /* rabota s /etc/passwd */ #include <sys/wait.h> /* opisanie formata wait() */ char cmd[256]; /* bufer dlya schityvaniya komandy */ #define MAXARGS 256 /* maks. kolichestvo argumentov */ char *arg[MAXARGS]; /* argumenty komandy */ char *fin, *fout; /* imena dlya perenapravleniya vvoda/vyvoda */ int rout; /* flagi perenapravleniya vyvoda */ char *firstfound; /* imya najdennoj, no nevypolnyaemoj programmy */ #define LIM ':' /* razdelitel' imen katalogov v path */ extern char *malloc(), *getenv(), *strcpy(), *getwd(); extern char *strchr(), *execat(); extern void callshell(), printenv(), setenv(), dowait(), setcwd(); extern struct passwd *getpwuid(); /* Predopredelennye peremennye */ extern char **environ; /* okruzhenie: iznachal'no smotrit na tot zhe * massiv, chto i ev iz main() */ extern int errno; /* kod oshibki sistemnogo vyzova */ char *strdup(s)char *s; { char *p; return(p=malloc(strlen(s)+1), strcpy(p,s)); } /* strcpy() vozvrashchaet svoj pervyj argument */ char *str3spl(s, p, q) char *s, *p, *q; { char *n = malloc(strlen(s)+strlen(p)+strlen(q)+1); strcpy(n, s); strcat(n, p); strcat(n, q); return n; } int cmps(s1, s2) char **s1, **s2; { return strcmp(*s1, *s2); } A. Bogatyrev, 1992-95 - 273 - Si v UNIX /* Perenapravit' vyvod */ #define APPEND 0x01 #define ERRTOO 0x02 int output (name, append, err_too, created) char *name; int *created; { int fd; *created = 0; /* Sozdan li fajl ? */ if( append ){ /* >>file */ /* Fajl name sushchestvuet? Probuem otkryt' na zapis' */ if((fd = open (name, O_WRONLY)) < 0) { if (errno == ENOENT) /* Fajl eshche ne sushchestvoval */ goto CREATE; else return 0; /* Ne imeem prava pisat' v etot fajl */ } /* inache fd == otkrytyj fajl, *created == 0 */ }else{ CREATE: /* Pytaemsya sozdat' (libo opustoshit') fajl "name" */ if((fd = creat (name, 0666)) < 0 ) return 0; /* Ne mogu sozdat' fajl */ else *created = 1; /* Byl sozdan novyj fajl */ } if (append) lseek (fd, 0l, 2); /* na konec fajla */ /* perenapravit' standartnyj vyvod */ dup2(fd, 1); if( err_too ) dup2(fd, 2); /* err_too=1 dlya >& */ close(fd); return 1; } /* Perenapravit' vvod */ int input (name) char *name; { int fd; if((fd = open (name, O_RDONLY)) < 0 ) return 0;/* Ne mogu chitat' */ /* perenapravit' standartnyj vvod */ dup2(fd, 0); close(fd); return 1; } A. Bogatyrev, 1992-95 - 274 - Si v UNIX /* zapusk komandy */ int cmdExec(progr, av, envp, inp, outp, outflg) char *progr; /* imya programmy */ char **av; /* spisok argumentov */ char **envp; /* okruzhenie */ char *inp, *outp; /* fajly vvoda-vyvoda (perenapravleniya) */ int outflg; /* rezhimy perenapravleniya vyvoda */ { void (*del)(), (*quit)(); int pid; int cr = 0; del = signal(SIGINT, SIG_IGN); quit = signal(SIGQUIT, SIG_IGN); if( ! (pid = fork())){ /* vetvlenie */ /* porozhdennyj process (syn) */ signal(SIGINT, SIG_DFL); /* vosstanovit' reakcii */ signal(SIGQUIT,SIG_DFL); /* po umolchaniyu */ /* getpid() vydaet nomer (identifikator) dannogo processa */ printf( "Process pid=%d zapushchen\n", pid = getpid()); /* Perenapravit' vvod-vyvod */ if( inp ) if(!input( inp )){ fprintf(stderr, "Ne mogu <%s\n", inp ); goto Err; } if( outp ) if(!output (outp, outflg & APPEND, outflg & ERRTOO, &cr)){ fprintf(stderr, "Ne mogu >%s\n", outp ); goto Err; } /* Zamenit' programmu: pri uspehe * dannaya programma zavershaetsya, a vmesto nee vyzyvaetsya * funkciya main(ac, av, envp) programmy, hranyashchejsya v fajle progr. * ac vychislyaet sistema. */ execvpe(progr, av, envp); Err: /* pri neudache pechataem prichinu i zavershaem porozhdennyj process */ perror(firstfound ? firstfound: progr); /* My ne delaem free(firstfound),firstfound = NULL * potomu chto dannyj process zavershaetsya (i tem VSYA ego * pamyat' osvobozhdaetsya) : */ if( cr && outp ) /* byl sozdan novyj fajl */ unlink(outp); /* no teper' on nam ne nuzhen */ exit(errno); } /* process - otec */ /* Sejchas signaly ignoriruyutsya, wait ne mozhet byt' oborvan * preryvaniem s klaviatury */ dowait(); /* ozhidat' okonchaniya syna */ /* vosstanovit' reakcii na signaly ot klaviatury */ signal(SIGINT, del); signal(SIGQUIT, quit); return pid; /* vernut' identifikator syna */ } A. Bogatyrev, 1992-95 - 275 - Si v UNIX /* Zapusk programmy s poiskom po peremennoj sredy PATH */ int execvpe(progr, av, envp) char *progr, **av, **envp; { char *path, *cp; int try = 1; register eacces = 0; char fullpath[256]; /* polnoe imya programmy */ firstfound = NULL; if((path = getenv("PATH")) == NULL ) path = ".:/bin:/usr/bin:/etc"; /* imya: korotkoe ili put' uzhe zadan ? */ cp = strchr(progr, '/') ? "" : path; do{ /* probuem raznye varianty */ cp = execat(cp, progr, fullpath); retry: fprintf(stderr, "probuem \"%s\"\n", fullpath ); execve(fullpath, av, envp); /* esli programma zapustilas', to na etom meste dannyj * process zamenilsya novoj programmoj. Inache - oshibka. */ switch( errno ){ /* kakova prichina neudachi ? */ case ENOEXEC: /* eto komandnyj fajl */ callshell(fullpath, av, envp); return (-1); case ETXTBSY: /* fajl zapisyvaetsya */ if( ++try > 5 ) return (-1); sleep(try); goto retry; case EACCES: /* ne imeete prava */ if(firstfound == NULL) firstfound = strdup(fullpath); eacces++; break; case ENOMEM: /* programma ne lezet v pamyat' */ case E2BIG: return (-1); } }while( cp ); if( eacces ) errno = EACCES; return (-1); } /* Sklejka ocherednoj komponenty path i imeni programmy name */ static char *execat(path, name, buf) register char *path, *name; char *buf; /* gde budet rezul'tat */ { register char *s = buf; while(*path && *path != LIM ) *s++ = *path++; /* imya kataloga */ if( s != buf ) *s++ = '/'; while( *name ) *s++ = *name++; /* imya programmy */ *s = '\0'; return ( *path ? ++path /* propustiv LIM */ : NULL ); } A. Bogatyrev, 1992-95 - 276 - Si v UNIX /* Zapusk komandnogo fajla pri pomoshchi vyzova interpretatora */ void callshell(progr, av, envp) char *progr, **av, **envp; { register i; char *sh; char *newav[MAXARGS+2]; int fd; char first = 0; if((fd = open(progr, O_RDONLY)) < 0 ) sh = "/bin/sh"; else{ read(fd, &first, 1); close(fd); sh = (first == '#') ? "/bin/csh" : "/bin/sh"; } newav[0] = "Shellscript"; newav[1] = progr; for(i=1; av[i]; i++) newav[i+1] = av[i]; newav[i+1] = NULL; printf( "Vyzyvaem %s\n", sh ); execve(sh, newav, envp); } /* Ozhidat' okonchaniya vseh processov, vydat' prichiny smerti. */ void dowait(){ int ws; int pid; while((pid = wait( &ws)) > 0 ){ if( WIFEXITED(ws)){ printf( "Process %d umer s kodom %d\n", pid, WEXITSTATUS(ws)); }else if( WIFSIGNALED(ws)){ printf( "Process %d ubit signalom %d\n", pid, WTERMSIG(ws)); if(WCOREDUMP(ws)) printf( "Obrazovalsya core\n" ); /* core - obraz pamyati processa dlya otladchika adb */ }else if( WIFSTOPPED(ws)){ printf( "Process %d ostanovlen signalom %d\n", pid, WSTOPSIG(ws)); } } } A. Bogatyrev, 1992-95 - 277 - Si v UNIX /* Rasshirenie shablonov imen. |to uproshchennaya versiya, kotoraya * rasshiryaet imena tol'ko v tekushchem kataloge. */ void glob(dir, args, indx, str /* chto rasshiryat' */, quote ) char *args[], *dir; int *indx; char *str; char quote; /* kavychki, v kotorye zaklyuchena stroka str */ { static char globchars[] = "*?["; char *p; char **start = &args[ *indx ]; short nglobbed = 0; register struct dirent *dirbuf; DIR *fd; extern DIR *opendir(); /* Zatychka dlya otmeny globbinga: */ if( *str == '\\' ){ str++; goto noGlob; } /* Obrabotka peremennyh $NAME */ if( *str == '$' && quote != '\'' ){ char *s = getenv(str+1); if( s ) str = s; } /* Analiz: trebuetsya li globbing */ if( quote ) goto noGlob; for( p=str; *p; p++ ) /* Est' li simvoly shablona? */ if( strchr(globchars, *p)) goto doGlobbing; noGlob: args[ (*indx)++ ] = strdup(str); return; doGlobbing: if((fd = opendir (dir)) == NULL){ fprintf(stderr, "Can't read %s\n", dir); return; } while ((dirbuf = readdir (fd)) != NULL ) { if (dirbuf->d_ino == 0) continue; if (strcmp (dirbuf->d_name, ".") == 0 || strcmp (dirbuf->d_name, "..") == 0) continue; if( match( dirbuf->d_name, str)){ args[ (*indx)++ ] = strdup(dirbuf->d_name); nglobbed++; } } closedir(fd); if( !nglobbed){ printf( "%s: no match\n", str); goto noGlob; }else{ /* otsortirovat' */ qsort(start, nglobbed, sizeof (char *), cmps); } } A. Bogatyrev, 1992-95 - 278 - Si v UNIX /* Razbor komandnoj stroki */ int parse(s) register char *s; { int i; register char *p; char tmp[80]; /* ocherednoj argument */ char c; /* ochistka staryh argumentov */ for(i=0; arg[i]; i++) free(arg[i]), arg[i] = NULL; if( fin ) free(fin ), fin = NULL; if( fout ) free(fout), fout = NULL; rout = 0; /* razbor stroki */ for( i=0 ;; ){ char quote = '\0'; /* propusk probelov - razdelitelej slov */ while((c = *s) && isspace(c)) s++; if( !c ) break; /* ocherednoe slovo */ p = tmp; if(*s == '\'' || *s == '"' ){ /* argument v kavychkah */ quote = *s++; /* simvol kavychki */ while((c = *s) != '\0' && c != quote){ if( c == '\\' ){ /* zaekranirovano */ c = *++s; if( !c ) break; } *p++ = c; ++s; } if(c == '\0') fprintf(stderr, "Net zakryvayushchej kavychki %c\n", quote); else s++; /* proignorirovat' kavychku na konce */ A. Bogatyrev, 1992-95 - 279 - Si v UNIX } else while((c = *s) && !isspace(c)){ if(c == '\\') /* zaekranirovano */ if( !(c = *++s)) break /* while */; *p++ = c; s++; } *p = '\0'; /* Proverit', ne est' li eto perenapravlenie * vvoda/vyvoda. V otlichie ot sh i csh * zdes' nado pisat' >FAJL <FAJL * >< vplotnuyu k imeni fajla. */ p = tmp; /* ocherednoe slovo */ if( *p == '>'){ /* perenapravlen vyvod */ p++; if( fout ) free(fout), rout = 0; /* uzhe bylo */ if( *p == '>' ){ rout |= APPEND; p++; } if( *p == '&' ){ rout |= ERRTOO; p++; } if( !*p ){ fprintf(stderr, "Net imeni dlya >\n"); fout = NULL; rout = 0; } else fout = strdup(p); } else if( *p == '<' ){ /* perenapravlen vvod */ p++; if( fin ) free(fin); /* uzhe bylo */ if( !*p ){ fprintf(stderr, "Net imeni dlya <\n"); fin = NULL; } else fin = strdup(p); } else /* dobavit' imena k argumentam */ glob( ".", arg, &i, p, quote ); } arg[i] = NULL; return i; } /* Ustanovit' imya pol'zovatelya */ void setuser(){ int uid = getuid(); /* nomer pol'zovatelya, zapustivshego SHell */ char *user = "mr. Nobody"; /* imya pol'zovatelya */ char *home = "/tmp"; /* ego domashnij katalog */ struct passwd *pp = getpwuid( uid ); if( pp != NULL ){ if(pp->pw_name && *pp->pw_name ) user = pp->pw_name; if( *pp->pw_dir ) home = pp->pw_dir; } setenv("USER", user); setenv("HOME", home); } void setcwd(){ /* Ustanovit' imya tekushchego kataloga */ char cwd[512]; getwd(cwd); setenv( "CWD", cwd ); } A. Bogatyrev, 1992-95 - 280 - Si v UNIX void main(ac, av, ev) char *av[], *ev[]; { int argc; /* kolichestvo argumentov */ char *prompt; /* priglashenie */ setuser(); setcwd(); signal(SIGINT, SIG_IGN); setbuf(stdout, NULL); /* otmenit' buferizaciyu */ for(;;){ prompt = getenv( "prompt" ); /* setenv prompt -->\ */ printf( prompt ? prompt : "@ ");/* priglashenie */ if( gets(cmd) == NULL /* at EOF */ ) exit(0); argc = parse(cmd); if( !argc) continue; if( !strcmp(arg[0], "exit" )) exit(0); if( !strcmp(arg[0], "cd" )){ char *d = (argc==1) ? getenv("HOME"):arg[1]; if(chdir(d) < 0) printf( "Ne mogu vojti v %s\n", d ); else setcwd(); continue; } if( !strcmp(arg[0], "echo" )){ register i; FILE *fp; if( fout ){ if((fp = fopen(fout, rout & APPEND ? "a":"w")) == NULL) continue; } else fp = stdout; for(i=1; i < argc; i++ ) fprintf( fp, "%s%s", arg[i], i == argc-1 ? "\n":" "); if( fp != stdout ) fclose(fp); continue; } if( !strcmp(arg[0], "setenv" )){ if( argc == 1 ) printenv(); else if( argc == 2 ) setenv( arg[1], "" ); else setenv( arg[1], arg[2]); continue; } cmdExec(arg[0], (char **) arg, environ, fin, fout, rout); } } A. Bogatyrev, 1992-95 - 281 - Si v UNIX /* -----------------------------------------------------------*/ /* Otsortirovat' i napechatat' okruzhenie */ void printenv(){ char *e[40]; register i = 0; char *p, **q = e; do{ p = e[i] = environ[i]; i++; } while( p ); #ifdef SORT qsort( e, --i /* skol'ko */, sizeof(char *), cmps); #endif while( *q ) printf( "%s\n", *q++ ); } /* Sravnenie imeni peremennoj okruzheniya s name */ static char *envcmp(name, evstr) char *name, *evstr; { char *p; int code; if((p = strchr(evstr, '=')) == NULL ) return NULL; /* error ! */ *p = '\0'; /* vremenno */ code = strcmp(name, evstr); *p = '='; /* vosstanovili */ return code==0 ? p+1 : NULL; } /* Ustanovit' peremennuyu okruzheniya */ void setenv( name, value ) char *name, *value; { static malloced = 0; /* 1, esli environ peremeshchen */ char *s, **p, **newenv; int len, change_at = (-1), i; /* Est' li peremennaya name v environ-e ? */ for(p = environ; *p; p++ ) if(s = envcmp(name, *p)){ /* uzhe est' */ if((len = strlen(s)) >= strlen(value)){ /* dostatochno mesta */ strcpy(s, value); return; } /* Esli eto novyj environ ... */ if( malloced ){ free( *p ); *p = str3spl(name, "=", value); return; } /* inache sozdaem kopiyu environ-a */ change_at = p - environ; /* indeks */ break; } A. Bogatyrev, 1992-95 - 282 - Si v UNIX /* Sozdaem kopiyu environ-a. Esli change_at == (-1), to * rezerviruem novuyu yachejku dlya eshche ne opredelennoj peremennoj */ for(p=environ, len=0; *p; p++, len++ ); /* vychislili kolichestvo peremennyh */ if( change_at < 0 ) len++; if((newenv = (char **) malloc( sizeof(char *) * (len+1))) == (char **) NULL) return; for(i=0; i < len+1; i++ ) newenv[i] = NULL; /* zachistka */ /* Kopiruem staryj environ v novyj */ if( !malloced ) /* ishodnyj environ v steke (dan sistemoj) */ for(i=0; environ[i]; i++ ) newenv[i] = strdup(environ[i]); else for(i=0; environ[i]; i++ ) newenv[i] = environ[i]; /* Vo vtorom sluchae stroki uzhe byli spaseny, kopiruem ssylki */ /* Izmenyaem, esli nado: */ if( change_at >= 0 ){ free( newenv[change_at] ); newenv[change_at] = str3spl(name, "=", value); } else { /* dobavit' v konec novuyu peremennuyu */ newenv[len-1] = str3spl(name, "=", value); } /* podmenit' environ */ if( malloced ) free( environ ); environ = newenv; malloced++; qsort( environ, len, sizeof(char *), cmps); } /* Dopishite komandy: unsetenv imya_peremennoj - udalyaet peremennuyu sredy; exit N - zavershaet interpretator s kodom vozvrata N (eto celoe chislo); */ A. Bogatyrev, 1992-95 - 283 - Si v UNIX 7. Tekstovaya obrabotka. Pod "tekstovoj obrabotkoj" (v protivoves "vychislitel'nym zadacham") zdes' ponima- etsya ogromnyj klass zadach obrabotki informacii nechislovogo haraktera, naprimer redak- tirovanie teksta, formatirovanie dokumentov, poisk i sortirovka, bazy dannyh, leksi- cheskij i sintaksicheskij analiz, pechat' na printere, preobrazovanie formata tablic, i.t.p. 7.1. Napishite programmu, "ugadyvayushchuyu" slovo iz zaranee zadannogo spiska po pervym neskol'kim bukvam. Vydajte soobshchenie "neodnoznachno", esli est' neskol'ko pohozhih slov. Uslozhnite programmu tak, chtoby spisok slov schityvalsya v programmu pri ee zapuske iz fajla list.txt 7.2. Napishite programmu, kotoraya udvaivaet probely v tekste s odinochnymi probelami. 7.3. Napishite programmu, kotoraya kopiruet vvod na vyvod, zamenyaya kazhduyu posledova- tel'nost' iz idushchih podryad neskol'kih probelov i/ili tabulyacij na odin probel. Shema ee resheniya shodna s resheniem sleduyushchej zadachi. 7.4. Napishite programmu podscheta slov v fajle. Slovo opredelite kak posledovatel'- nost' simvolov, ne vklyuchayushchuyu simvoly probela, tabulyacii ili novoj stroki. "Kanoni- cheskij" variant resheniya, privedennyj u Kernigana i Ritchi, takov: #include <ctype.h> #include <stdio.h> const int YES=1, NO=0; main(){ register int inWord = NO; /* sostoyanie */ int words = 0, c; while((c = getchar()) != EOF) if(isspace(c) || c == '\n') inWord = NO; else if(inWord == NO){ inWord = YES; ++words; } printf("%d slov\n", words); } Obratite vnimanie na konstrukciyu const. |to ob®yavlenie imen kak konstant. |ta konst- rukciya blizka k #define YES 1 no pozvolyaet kompilyatoru - bolee strogo proveryat' tip, t.k. eto tipizirovannaya konstanta; - sozdavat' bolee ekonomnyj kod; - zapreshchaet izmenyat' eto znachenie. Rassmotrim primer main(){ /* cc 00.c -o 00 -lm */ double sqrt(double); const double sq12 = sqrt(12.0); #define SQRT2 sqrt(2.0) double x; x = sq12 * sq12 * SQRT2 * SQRT2; /* @1 */ sq12 = 3.4641; /* @2 */ printf("%g %g\n", sq12, x); } Ispol'zovanie #define prevratit stroku @1 v x = sq12 * sq12 * sqrt(2.0) * sqrt(2.0); to est' sozdast kod s dvumya vyzovami funkcii sqrt. Konstrukciya zhe const zanosit vychislennoe vyrazhenie v yachejku pamyati i dalee prosto ispol'zuet ee znachenie. Pri etom A. Bogatyrev, 1992-95 - 284 - Si v UNIX kompilyator ne pozvolyaet vposledstvii izmenyat' eto znachenie, poetomu stroka @2 oshi- bochna. Teper' predlozhim eshche odnu programmu podscheta slov, gde slovo opredelyaetsya makro- som isWord, perechislyayushchim bukvy dopustimye v slove. Programma osnovana na pereklyucha- tel'noj tablice funkcij (etot podhod primenim vo mnogih sluchayah): #include <ctype.h> #include <stdio.h> int wordLength, inWord, words; /* = 0 */ char aWord[128], *wrd; void space (c){} void letter (c){ wordLength++; *wrd++ = c; } void begWord(c){ wordLength=0; inWord=1; wrd=aWord; words++; letter(c); } void endWord(c){ inWord=0; *wrd = '\0'; printf("Slovo '%s' dliny %d\n", aWord, wordLength); } void (*sw[2][2])() = { /* !isWord */ { space, endWord }, /* isWord */ { begWord, letter } /* !inWord inWord */ }; #define isWord(c) (isalnum(c) || c=='-' || c=='_') main(){ register c; while((c = getchar()) != EOF) (*sw[isWord(c)][inWord])(c); printf("%d slov\n", words); } 7.5. Napishite programmu, vydayushchuyu gistogrammu dlin strok fajla (t.e. tablicu: strok dliny 0 stol'ko-to, dliny 1 - stol'ko-to, i.t.p., prichem tablicu mozhno izobrazit' graficheski). 7.6. Napishite programmu, kotoraya schityvaet slovo iz fajla in i zapisyvaet eto slovo v konec fajla out. 7.7. Napishite programmu, kotoraya budet pechatat' slova iz fajla vvoda, prichem po odnomu na stroku. 7.8. Napishite programmu, pechatayushchuyu gistogrammu dlin slov iz fajla vvoda. 7.9. Napishite programmu, chitayushchuyu slova iz fajla i razmeshchayushchuyu ih v vide dvunaprav- lennogo spiska slov, otsortirovannogo po alfavitu. Ukazaniya: ispol'zujte dinamicheskuyu pamyat' (malloc) i ukazateli; napishite funkciyu vklyucheniya novogo slova v spisok na nuzh- noe mesto. V konce raboty raspechatajte spisok dvazhdy: v pryamom i v obratnom poryadke. Uslozhnenie: ne hranit' v spiske dublikaty; vmesto etogo vmeste so slovom hranit' schetchik kolichestva ego vhozhdenij v tekst. 7.10. Napishite programmu, kotoraya pechataet slova iz svoego fajla vvoda, raspolozhen- nye v poryadke ubyvaniya chastoty ih poyavleniya. Pered kazhdym slovom napechatajte chislo chastoty ego poyavleniya. 7.11. Napishite programmu, chitayushchuyu fajl postrochno i pechatayushchuyu slova v kazhdoj stroke v obratnom poryadke. A. Bogatyrev, 1992-95 - 285 - Si v UNIX 7.12. Napishite programmu kopirovaniya vvoda na vyvod takim obrazom, chtoby iz kazhdoj gruppy posledovatel'no odinakovyh strok vyvodilas' tol'ko odna stroka. |to analog programmy uniq v sisteme UNIX. Otvet: #include <stdio.h> /* char *gets(); */ char buf1[4096], buf2[4096]; char *this = buf1, *prev = buf2; main(){ long nline =0L; char *tmp; while( gets(this)){ if(nline){ /* sravnit' novuyu i predydushchuyu stroki */ if( strcmp(this, prev)) /* razlichny ? */ puts(prev); } /* obmen buferov: */ tmp=prev; prev=this; this=tmp; nline++; /* nomer stroki */ }/* endwhile */ if( nline ) puts(prev); /* poslednyaya stroka vsegda vydaetsya */ } 7.13. Sostav'te programmu, kotoraya budet udalyat' v konce (i v nachale) kazhdoj stroki fajla probely i tabulyacii, a takzhe udalyat' stroki, celikom sostoyashchie iz probelov i tabulyacij. 7.14. Dlya ekonomii mesta v fajle, redaktory tekstov pri zapisi otredaktirovannogo fajla szhimayut podryad idushchie probely v tabulyaciyu. CHasto eto neudobno dlya programm obrabotki tekstov (poskol'ku trebuet osoboj obrabotki tabulyacij - eto ODIN simvol, kotoryj na ekrane i v tekste zanimaet NESKOLXKO pozicij!), poetomu pri chtenii fajla my dolzhny rasshiryat' tabulyacii v nuzhnoe kolichestvo probelov, naprimer tak: /* zamenyat' tabulyacii na probely */ void untab(s) register char *s; { char newstr[256]; /* novaya stroka */ char *src = s; int n; /* schetchik */ register dstx; /* koordinata x v novoj stroke */ for(dstx = 0; *s != '\0'; s++) if( *s == '\t'){ for(n = 8 - dstx % 8 ; n > 0 ; n--) newstr[dstx++] = ' '; }else newstr[dstx++] = *s; newstr[dstx] = '\0'; strcpy(src, newstr); /* stroku na staroe mesto */ } 7.15. Napishite obratnuyu funkciyu, szhimayushchuyu podryad idushchie probely v tabulyacii. A. Bogatyrev, 1992-95 - 286 - Si v UNIX void tabify(){ int chr; int icol, ocol; /* input/output columns */ for(icol = ocol = 0; ; ){ if((chr = getchar()) == EOF) break; switch(chr){ case ' ': icol++; break; case '\n': case '\r': ocol = icol = 0; putchar(chr); break; case '\t': icol += 8; icol &= ~07; /* icol -= icol % 8; */ break; default: while(((ocol + 8) & ~07) <= icol){ #ifdef NOTDEF if(ocol + 1 == icol) break; /* vzyat' ' ' vmesto '\t' */ #endif putchar('\t'); ocol += 8; ocol &= ~07; } while(ocol < icol){ putchar(' '); ocol++; } putchar(chr); icol++; ocol++; break; } } } 7.16. Sostav'te programmu, ukorachivayushchuyu stroki ishodnogo fajla do zadannoj velichiny i pomeshchayushchuyu rezul'tat v ukazannyj fajl. Uchtite, chto tabulyaciya razvorachivaetsya v nes- kol'ko probelov! 7.17. Razrabotajte programmu, ukorachivayushchuyu stroki vhodnogo fajla do 60 simvolov. Odnako teper' zapreshchaetsya obrubat' slova. A. Bogatyrev, 1992-95 - 287 - Si v UNIX 7.18. Razrabotajte programmu, zapolnyayushchuyu promezhutki mezhdu slovami stroki dopolni- tel'nymi probelami takim obrazom, chtoby dlina stroki byla ravna 60 simvolam. 7.19. Napishite programmu, perenosyashchuyu slishkom dlinnye stroki. Slova razbivat' nel'zya (neumeshayushcheesya slovo sleduet perenesti celikom). SHirinu stroki schitat' ravnoj 60. 7.20. Sostav'te programmu, centriruyushchuyu stroki fajla otnositel'no serediny ekrana, t.e. dobavlyayushchuyu v nachalo stroki takoe kolichestvo probelov, chtoby seredina stroki pechatalas' v 40-oj pozicii (schitaem, chto obychnyj ekran imeet shirinu 80 simvolov). 7.21. Napishite programmu, otsekayushchuyu n probelov v nachale kazhdoj stroki (ili n pervyh lyubyh simvolov). Uchtite, chto v fajle mogut byt' stroki koroche n (naprimer pustye stroki). #include <stdio.h> /* ... tekst funkcii untab(); ... */ void process(char name[], int n, int spacesOnly){ char line[256]; int length, shift, nline = 0; char newname[128]; FILE *fpin, *fpout; if((fpin = fopen(name, "r")) == NULL){ fprintf(stderr, "Ne mogu chitat' %s\n", name); return; } sprintf(newname, "_%s", name); /* naprimer */ if((fpout = fopen(newname, "w")) == NULL){ fprintf(stderr, "Ne mogu sozdat' %s\n", newname); fclose(fpin); return; } while(fgets(line, sizeof line, fpin)){ ++nline; if((length = strlen(line)) && line[length-1] == '\n') line[--length] = '\0'; /* obrubit' '\n' */ untab(line); /* razvernut' tabulyacii */ for(shift=0; line[shift] != '\0' && shift < n ; ++shift) if(spacesOnly && line[shift] != ' ') break; if(*line && shift != n ) /* Preduprezhdenie */ fprintf(stderr, "Nachalo stroki #%d slishkom korotko\n", nline); fprintf(fpout, "%s\n", line+shift); /* nel'zya bylo fputs(line+n, fpout); * t.k. eta poziciya mozhet byt' ZA koncom stroki */ } fclose(fpin); fclose(fpout); } void main(int argc, char **argv){ if( argc != 3 ) exit(1); process(argv[2], atoi(argv[1]) /* 8 */, 1); exit(0); } 7.22. Napishite programmu, razbivayushchuyu fajl na dva po vertikali: v pervyj fajl popa- daet levaya polovina ishodnogo fajla, vo vtoroj - pravaya. SHirinu kolonki zadavajte iz argumentov main(). Esli zhe argument ne ukazan - 40 pozicij. 7.23. Napishite programmu sortirovki strok v alfavitnom poryadke. Uchtite, chto funkciya strcmp() sravnivaet stroki v poryadke kodirovki, prinyatoj na dannoj konkretnoj mashine. Russkie bukvy, kak pravilo, idut ne v alfavitnom poryadke! Sleduet napisat' funkciyu A. Bogatyrev, 1992-95 - 288 - Si v UNIX dlya alfavitnogo sravneniya otdel'nyh simvolov i, pol'zuyas' eyu, perepisat' funkciyu strcmp(). 7.24. Otsortirujte massiv strok po leksikograficheskomu ubyvaniyu, ignoriruya razlichiya mezhdu strochnymi i propisnymi bukvami. 7.25. Sostav'te programmu dihotomicheskogo poiska v otsortirovannom massive strok (metodom deleniya popolam). /* Poisk v tablice metodom polovinnogo deleniya: dihotomia */ #include <stdio.h> struct elem { char *name; /* klyuch poiska */ int value; } table[] = { /* imena strogo po alfavitu */ { "andrew", 17 }, { "bill", 23 }, { "george", 55 }, { "jack", 54 }, { "jaw", 43 }, { "john", 33 }, { "mike", 99 }, { "paul", 21 }, { "sue", 66 }, /* SIZE - 2 */ { NULL, -1 }, /* SIZE - 1 */ /* NULL vveden tol'ko dlya raspechatki tablicy */ }; #define SIZE (sizeof(table) / sizeof(struct elem)) /* Dihotomicheskij poisk po tablice */ struct elem *find(s, table, size) char *s; /* chto najti ? */ struct elem table[]; /* v chem ? */ int size; /* sredi pervyh size elementov */ { register top, bottom, middle; register code; top = 0; /* nachalo */ bottom = size - 1; /* konec: indeks stroki "sue" */ while( top <= bottom ){ middle = (top + bottom) / 2; /* seredina */ /* sravnit' stroki */ code = strcmp( s, table[middle].name ) ; if( code > 0 ){ top = middle + 1; }else if( code < 0 ){ bottom = middle - 1; }else return &table[ middle ]; } return (struct elem *) NULL; /* ne nashel */ } A. Bogatyrev, 1992-95 - 289 - Si v UNIX /* raspechatka tablicy */ void printtable(tbl) register struct elem *tbl; { for( ; tbl->name != NULL ; tbl++ ){