Книга: Разработка приложений в среде Linux. Второе издание

23.2.3. Простая утилита grep

23.2.3. Простая утилита grep
grep
является популярной утилитой, определенной в стандарте POSIX, которая предлагает возможности поиска регулярного выражения в текстовых файлах. Ниже показана простая (не соответствующая стандарту POSIX) версия утилиты grep, реализованная с помощью функций стандартного регулярного выражения.

  1: /* grep.с */
  2:
  3: #include <alloca.h>
  4: #include <ctype.h>
  5: #include <popt.h>
  6: #include <regex.h>
  7: #include <stdio.h>
  8: #include <string.h>
  9: #include <unistd.h>
 10:
 11: #define MODE_REGEXP 1
 12: #define MODE_EXTENDED 2
 13: #define MODE_FIXED 3
 14:
 15: void do_regerror(int errcode, const regex_t *preg) {
 16:  char *errbuf;
 17:  size_t errbuf_size;
 18:
 19:  errbuf_size = regerror(errcode, preg, NULL, 0);
 20:  errbuf = alloca(errbuf_size);
 21:  if (!errbuf) {
 22:   perror("alloca");
 23:   return;
 24:  }
 25:
 26:  regerror(errcode, preg, errbuf, errbuf_size);
 27:  fprintf(stderr, "%sn", errbuf);
 28: }
 29:
 30: int scanFile(FILE * f, int mode, const void * pattern,
 31:  int ignoreCase, const char * fileName,
 32:  int * maxCountPtr) {
 33:  long lineLength;
 34:  char * line;
 35:  int match;
 36:  int rc;
 37:  char * chptr;
 38:  char * prefix = "";
 39:
 40:  if (fileName) {
 41:   prefix = alloca(strlen(fileName) + 4);
 42:   sprintf(prefix, "%s: ", fileName);
 43:  }
 44:
 45:  lineLength = sysconf(_SC_LINE_MAX);
 46:  line = alloca(lineLength);
 47:
 48:  while (fgets(line, lineLength, f) && (*maxCountPtr)) {
 49:   /* если у нас не будет завершающего символа 'n'
 50:      то мы не сможем получить всю строку целиком */
 51:   if (line [strlen (line) -1] != 'n') {
 52:    fprintf(stderr, " %s line слишком длиннаяn", prefix);
 53:    return 1;
 54:   }
 55:
 56:   if (mode == MODE_FIXED) {
 57:    if (ignoreCase) {
 58:     for (chptr = line; *chptr; chptr++) {
 59:      if (isalpha(*chptr)) *chptr = tolower(*chptr);
 60:     }
 61:    }
 62:    match = (strstr(line, pattern) != NULL);
 63:   } else {
 64:    match = 0;
 65:    rc = regexec (pattern, line, 0, NULL, 0);
 66:    if (!rc)
 67:     match = 1;
 68:    else if (rc != REG_NOMATCH)
 69:    do_regerror(match, pattern);
 70:   }
 71:
 72:   if (match) {
 73:    printf("%s%s", prefix, line);
 74:    if (*maxCountPtr > 0)
 75:     (*maxCountPtr)--;
 76:   }
 77:  }
 78:
 79:  return 0;
 80: }
 81:
 82: int main(int argc, const char ** argv) {
 83:  const char * pattern = NULL;
 84:  regex_t regPattern;
 85:  const void * finalPattern;
 86:  int mode = MODE_REGEXP;
 87:  int ignoreCase = 0;
 88:  int maxCount = -1;
 89:  int rc;
 90:  int regFlags;
 91:  const char ** files;
 92:  poptContext optCon;
 93:  FILE * f;
 94:  char * chptr;
 95:  struct poptOption optionsTable[] = {
 96:   { "extended-regexp", 'E', POPT_ARG_VAL,
 97:     &mode, MODE_EXTENDED,
 98:     "шаблоном для соответствия является расширенное регулярное "
 99:     "выражение"},
100:   { "fixed-strings", 'F', POPT_ARG_VAL,
101:     &mode, MODE_FIXED,
102:     "шаблоном для соответствия является базовая строка (не "
103:     "регулярное выражение)", NULL },
104:   { "basic-regexp", 'G', POPT_ARG_VAL,
105:     &mode, MODE_REGEXP,
106:     "шаблоном для соответствия является базовое регулярное выражение" },
107:   { "ignore-case", 'i', POPT_ARG_NONE, &ignoreCase, 0,
108:     "выполнять поиск, чувствительный к регистру", NULL },
109:   { "max-count", 'm', POPT_ARG_INT, &maxCount, 0,
110:     "завершить после получения N. совпадений", "N" },
111:   { "regexp", 'e', POPT_ARG_STRING, &pattern, 0,
112:     "регулярное выражение для поиска", "pattern" },
113:     POPT_AUTOHELP
114:   { NULL, '', POPT_ARG_NONE, NULL, 0, NULL, NULL }
115:  };
116:
117:  optCon = poptGetContext("grep", argc, argv, optionsTable, 0);
118:  poptSetOtherOptionHelp(optCon, "<шаблон> <список файлов>");
119:
120:  if ((rc = poptGetNextOpt(optCon)) < -1) {
121:   /* во время обработки параметра возникла ошибка */
122:   fprintf(stderr, "%s: %sn",
123:    poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
124:   poptStrerror(rc));
125:   return 1;
126:  }
127:
128:  files = poptGetArgs(optCon);
129:  /* если мы не получили шаблон, то он должен быть первым
130:     из оставшихся */
131:  if (!files && !pattern) {
132:   poptPrintUsage(optCon, stdout, 0);
133:   return 1;
134:  }
135:
136:  if (!pattern) {
137:   pattern = files[0];
138:   files++;
139:  }
140:
141:  regFlags = REG_NEWLINE | REG_NOSUB;
142:  if (ignoreCase) {
143:   regFlags |= REG_ICASE;
144:   /* преобразование шаблона в нижний регистр; этого можно не делать,
145:      если мы игнорируем регистр в регулярном выражении, однако позволяет
146:      функции strstr() правильно обработать -i */
147:   chptr = alloca(strlen(pattern) + 1);
148:   strcpy(chptr, pattern);
149:   pattern = chptr;
150:
151:   while (*chptr) {
152:    if (isalpha(*chptr)) *chptr = tolower(*chptr);
153:    chptr++;
154:   }
155:  }
156:
157:
158:  switch (mode) {
159:  case MODE_EXTENDED:
160:   regFlags |= REG_EXTENDED;
161:  case MODE_REGEXP:
162:   if ((rc = regcomp(&regPattern, pattern, regFlags))) {
163:    do_regerror(rc, &regPattern);
164:    return 1;
165:   }
166:   finalPattern = &regPattern;
167:   break;
168:
169:  case MODE_FIXED:
170:   finalPattern = pattern;
171:   break;
172:  }
173:
174:  if (!*files) {
175:   rc = scanFile(stdin, mode, finalPattern, ignoreCase, NULL,
176:    &maxCount);
177:  } else if (!files[1]) {
178:   /* эта часть обрабатывается отдельно, поскольку имя файла
179:      выводить не нужно */
180:   if (!(f = fopen(*files, "r"))) {
181:    perror(*files);
182:    rc = 1;
183:   } else {
184:    rc = scanFile(f, mode, finalPattern, ignoreCase, NULL,
185:     &maxCount);
186:    fclose(f);
187:   }
188:  } else {
189:   rc = 0;
190:
191:   while (*files) {
192:    if (!(f = fopen(*files, "r"))) {
193:     perror(*files);
194:     rc = 1;
195:    } else {
196:     rc |= scanFile(f, mode, finalPattern, ignoreCase,
197:      *files, &maxCount);
198:     fclose(f);
199:    }
200:    files++;
201:    if (!maxCount) break;
202:   }
203:  }
204:
205:  return rc;
206: }

Оглавление книги


Генерация: 1.272. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз