Новые книги

Находясь на переднем крае программирования, книга «Программист-прагматик. Путь от подмастерья к мастеру» абстрагируется от всевозрастающей специализации и технических тонкостей разработки программ на современном уровне, чтобы исследовать суть процесса – требования к работоспособной и поддерживаемой программе, приводящей пользователей в восторг. Книга охватывает различные темы – от личной ответственности и карьерного роста до архитектурных методик, придающих программам гибкость и простоту в адаптации и повторном использовании.

Прочитав эту книгу, вы научитесь:

Бороться с недостатками программного обеспечения;

Избегать ловушек, связанных с дублированием знания;

Создавать гибкие, динамичные и адаптируемые программы;

Избегать программирования в расчете на совпадение;

Защищать вашу программу при помощи контрактов, утверждений и исключений;

Собирать реальные требования;

Осуществлять безжалостное и эффективное тестирование;

Приводить в восторг ваших пользователей;

Формировать команды из программистов-прагматиков и с помощью автоматизации делать ваши разработки более точными.
Дорогой читатель, перед тобой уникальная книга, в которой нам удалось собрать опыт всех тренингов по продажам, которые проводили бизнес-тренеры Петербургской Школы переговорщиков «ШИП».

Мы разберем техники и основные ошибки, которые совершают люди при проведении продаж, посмотрим, как по-разному подходят к процессу продаж различные компании, увидим, на чем строится работа эффективного продавца.

Для простоты понимания, мы будем использовать в тексте все те схемы, таблицы, которые мы используем на тренингах.

Для кого эта книга?

Для тех, кто только собирается стать миллионером, путем продажи товара или услуги.

Эта книга для тех кто, активно занимается продажами, для кого они источник материального благополучия.

Эта книга для специалистов корпоративных университетов, которые обучают продажам в компании, кому важно в короткие сроки донести ключевую информацию до менеджеров по продажам, продавцов.

Эта книга для руководителей отделов продаж, проектных групп, чьи сотрудники постоянно отстаивают интересы компании в переговорах с покупателями.

Эта книга для hr-специалистов, для тех, кто занимается отбором специалистов в отделы продаж, кому важно быстро принять правильное решение, для чего нужно понять, будет ли из кандидата толк, сможет он приносить прибыль компании или будет ненужным балластом.

Эта книга для бизнес-тренера, которому важно уметь емко и сжато объяснить участникам тренинга по продажам, что же такое «продажи», и научить их зарабатывать деньги для себя и компании.

Книга поможет не только разобраться в принципах эффективных продаж, но и оценить свои способности благодаря уникальным тестам, приведенным в конце книги.

Системные вызовы и взаимодействие с UNIX.



6.1.5. Напишите функцию рекурсивного обхода дерева подкаталогов и печати имен всех файлов в нем. Ключ U42 означает файловую систему с длинными именами файлов (BSD 4.2).

    /*#!/bin/cc -DFIND -DU42 -DMATCHONLY treemk.c match.c -o tree -lx
     * Обход поддерева каталогов (по мотивам Керниган & Ритчи).
     *              Ключи компиляции:
     * BSD-4.2 BSD-4.3                     -DU42
     * XENIX с канонической файл.сист.      ничего
     * XENIX с библиотекой  -lx            -DU42
     *      программа поиска файлов                          -DFIND
     *      программа рекурсивного удаления                  -DRM_REC
     *      программа подсчета используемого места на диске  БЕЗ_КЛЮЧА
     */
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <sys/param.h>         /* для MAXPATHLEN */
    #if defined(M_XENIX) && defined(U42)
    # include <sys/ndir.h>  /* XENIX + U42 эмуляция */
    #else
    # include <dirent.h>
    # define stat(f,s) lstat(f,s)  /* не проходить по символьным ссылкам */
    # define d_namlen d_reclen
    #endif
    /* проверка: каталог ли это */
    #define  isdir(st) ((st.st_mode & S_IFMT) == S_IFDIR)
    struct   stat st;               /* для сисвызова stat() */
    char     buf[MAXPATHLEN+1];     /* буфер для имени файла */
    #define FAILURE (-1)            /* код неудачи */
    #define SUCCESS   1             /* код успеха  */
    #define WARNING   0             /* нефатальная ошибка */
    /* Сообщения об ошибках во время обхода дерева: */
    #ifndef ERR_CANT_READ
    # define ERR_CANT_READ(name)  \
             fprintf( stderr, "\tНе могу читать \"%s\"\n", name), WARNING
    # define ERR_NAME_TOO_LONG()  \
             fprintf( stderr, "\tСлишком длинное полное имя\n" ), WARNING
    #endif
    /* Прототипы для предварительного объявления функций. */
    extern char *strrchr(char *, char);
    int directory (char *name, int level,
        int (*enter)(char *full, int level, struct stat *st),
        int (*leave)(char *full, int level),
        int (*touch)(char *full, int level, struct stat *st));
    /* Функции-обработчики enter, leave, touch должны
     * возвращать (-1) для прерывания просмотра дерева,
     * либо значение >= 0 для продолжения. */
    /* Обойти дерево с корнем в rootdir */
    int walktree (
        char *rootdir,      /* корень дерева */
        int (*enter)(char *full, int level, struct stat *st),
        int (*leave)(char *full, int level),
        int (*touch)(char *full, int level, struct stat *st)
    ){
        /* проверка корректности корня */
        if( stat(rootdir, &st) < 0 || !isdir(st)){
            fprintf( stderr, "\tПлохой корень дерева \"%s\"\n", rootdir );
            return   FAILURE;  /* неудача */
        }
        strcpy     (buf, rootdir);
        return act (buf, 0, enter, leave, touch);
    }
    /* Оценка файла с именем name.
     */
    int act (char *name, int level,
        int (*enter)(char *full, int level, struct stat *st),
        int (*leave)(char *full, int level),
        int (*touch)(char *full, int level, struct stat *st))
    {
        if (stat (name, &st) < 0)
            return WARNING; /* ошибка, но не фатальная      */
        if(isdir(st)){      /* позвать обработчик каталогов */
           if(enter)
              if( enter(name, level, &st) == FAILURE ) return FAILURE;
           return directory (name, level+1, enter, leave, touch);
        } else {            /* позвать обработчик файлов    */
           if(touch) return touch (name, level, &st);
           else      return SUCCESS;
        }
    }
    /* Обработать каталог: прочитать его и найти подкаталоги */
    int directory (char *name, int level,
        int (*enter)(char *full, int level, struct stat *st),
        int (*leave)(char *full, int level),
        int (*touch)(char *full, int level, struct stat *st))
    {
    #ifndef U42
        struct direct   dirbuf;
        int        fd;
    #else
        register struct dirent *dirbuf;
        DIR    *fd;
        extern DIR *opendir();
    #endif
        char   *nbp, *tail, *nep;
        int     i, retcode = SUCCESS;
    #ifndef U42
        if ((fd = open (name, 0)) < 0) {
    #else
        if ((fd = opendir (name)) == NULL) {
    #endif
            return ERR_CANT_READ(name);
        }
        tail = nbp = name + strlen (name);  /* указатель на закрывающий \0 */
        if( strcmp( name, "/" ))  /* если не "/" */
            *nbp++ = '/';
        *nbp = '\0';
    #ifndef U42
        if (nbp + DIRSIZ + 2 >= name + MAXPATHLEN) {
            *tail = '\0';
            return ERR_NAME_TOO_LONG();
        }
    #endif
    #ifndef U42
        while (read(fd, (char *) &dirbuf, sizeof(dirbuf)) == sizeof(dirbuf)){
            if (dirbuf.d_ino == 0)  /* стертый файл */
                continue;
            if (strcmp (dirbuf.d_name, "." ) == 0  ||
                strcmp (dirbuf.d_name, "..") == 0)  /* не интересуют */
                continue;
            for (i = 0, nep = nbp; i < DIRSIZ; i++)
                *nep++ = dirbuf.d_name[i];
    # else /*U42*/
        while ((dirbuf = readdir (fd)) != NULL ) {
            if (dirbuf->d_ino == 0)
                continue;
            if (strcmp (dirbuf->d_name, "." ) == 0  ||
                strcmp (dirbuf->d_name, "..") == 0)
                continue;
            for (i = 0, nep = nbp; i < dirbuf->d_namlen ; i++)
                *nep++ = dirbuf->d_name[i];
    #endif /*U42*/
            *nep = '\0';
            if( act(name, level,  enter, leave, touch) == FAILURE) {
                retcode = FAILURE; break;                          }
        }
    #ifndef U42
        close (fd);
    #else
        closedir(fd);
    #endif
        *tail = '\0';       /* восстановить старое name */
        if(retcode != FAILURE   &&   leave)
           if( leave(name, level) == FAILURE) retcode = FAILURE;
        return retcode;
    }
    /* -------------------------------------------------------------- */
    /* Disk Usage -- Оценка места, занимаемого файлами поддерева      */
    /* -------------------------------------------------------------- */
    /* Пересчет байтов в килобайты */
    #define KB(s)  (((s)/1024L) + ((s)%1024L ? 1L:0L))
    /* или #define KB(s)   (((s) + 1024L - 1) / 1024L)  */
    long size;                      /* общий размер     */
    long nfiles;                    /* всего файлов     */
    long ndirs;                     /* из них каталогов */
    #define WARNING_LIMIT 150L      /* подозрительно большой файл */
    static int du_touch (char *name, int level, struct stat *st){
         long sz;
         size += (sz = KB(st->st_size));  /* размер файла в Кб. */
         nfiles++;
    #ifndef TREEONLY
         if( sz >= WARNING_LIMIT )
            fprintf(stderr,"\tВнимание! \"%s\" очень большой: %ld Кб.\n",
                                          name,               sz);
    #endif /*TREEONLY*/
         return SUCCESS;
    }
    static int du_enter (char *name, int level, struct stat *st){
    #ifndef TREEONLY
         fprintf( stderr, "Каталог \"%s\"\n", name );
    #endif
         size += KB(st->st_size);  /* размер каталога в Кб. */
         nfiles++; ++ndirs; return SUCCESS;
    }
    long du (char *name){
         size = nfiles = ndirs = 0L;
         walktree(name, du_enter, NULL, du_touch );
         return size;
    }
    /* -------------------------------------------------------------- */
    /* Рекурсивное удаление файлов и каталогов                        */
    /* -------------------------------------------------------------- */
    int  deleted;    /* сколько файлов и каталогов удалено */
    static int recrm_dir (char *name, int level){
         if( rmdir(name) >= 0){ deleted++; return SUCCESS; }
         fprintf(stderr, "Не могу rmdir '%s'\n", name); return WARNING;
    }
    static int recrm_file(char *name, int level, struct stat *st){
         if( unlink(name) >= 0){ deleted++; return SUCCESS; }
         fprintf(stderr, "Не могу rm    '%s'\n", name); return WARNING;
    }
    int recrmdir(char *name){
        int ok_code; deleted = 0;
        ok_code = walktree(name, NULL, recrm_dir, recrm_file);
        printf("Удалено %d файлов и каталогов в %s\n", deleted, name);
        return ok_code;
    }
    /* -------------------------------------------------------------- */
    /* Поиск файлов с подходящим именем (по шаблону имени)            */
    /* -------------------------------------------------------------- */
    char *find_PATTERN;
    static int find_check(char *fullname, int level, struct stat *st){
        char *basename = strrchr(fullname, '/');
        if(basename) basename++;
        else         basename = fullname;
        if( match(basename, find_PATTERN))
            printf("Level#%02d %s\n", level, fullname);
        if( !strcmp( basename, "core")){
            printf("Найден дамп %s, поиск прекращен.\n", fullname);
            return FAILURE;
        }
        return SUCCESS;
    }
    void find (char *root, char *pattern){
         find_PATTERN = pattern;
         walktree(root, find_check, NULL, find_check);
    }
    /* -------------------------------------------------------------- */
    #ifndef TREEONLY
    void main(int argc, char *argv[]){
    #ifdef FIND
         if(argc != 3){ fprintf(stderr, "Arg count\n"); exit(1); }
         find(argv[1], argv[2]);
    #else
    # ifdef RM_REC
         for(argv++; *argv; argv++)
             recrmdir(*argv);
    # else
         du( argc == 1 ? "." : argv[1] );
         printf( "%ld килобайт в %ld файлах.\n", size, nfiles );
         printf( "%ld каталогов.\n", ndirs );
    # endif
    #endif
         exit(0);
    }
    #endif /*TREEONLY*/

6.1.6. Используя предыдущий алгоритм, напишите программу рекурсивного копирования поддерева каталогов в другое место. Для создания новых каталогов используйте системный вызов

    mkdir(имя_каталога, коды_доступа);

6.1.7. Используя тот же алгоритм, напишите программу удаления каталога, которая удаляет все файлы в нем и, рекурсивно, все его подкаталоги. Таким образом, удаляется дерево каталогов. В UNIX подобную операцию выполняет команда

    rm -r имя_каталога_корня_дерева

6.1.8. Используя все тот же алгоритм обхода, напишите аналог команды find, который будет позволять:

  • находить все файлы, чьи имена удовлетворяют заданному шаблону (используйте функцию match() из главы "Текстовая обработка");
  • находить все выполняемые файлы: обычные файлы S_IFREG, у которых
            (st.st_mode & 0111) != 0

Как уже ясно, следует пользоваться вызовом stat для проверки каждого файла.

6.2. Время в UNIX.

6.2.1. Напишите функцию, переводящую год, месяц, день, часы, минуты и секунды в число секунд, прошедшее до указанного момента с 00 часов 00 минут 00 секунд 1 Января 1970 года. Внимание: результат должен иметь тип long (точнее time_t).

Эта функция облегчит вам сравнение двух моментов времени, заданных в общепринятом "человеческом" формате, поскольку сравнить два long числа гораздо проще, чем сравнивать по очереди годы, затем, если они равны - месяцы, если месяцы равны - даты, и.т.д.; а также облегчит измерение интервала между двумя событиями - он вычисляется просто как разность двух чисел. В системе UNIX время обрабатывается и хранится именно в виде числа секунд; в частности текущее астрономическое время можно узнать системным вызовом

    #include <sys/types.h>
    #include <time.h>
    time_t t = time(NULL);  /* time(&t); */

Функция

    struct tm *tm = localtime( &t );

разлагает число секунд на отдельные составляющие, содержащиеся в int-полях структуры:

    tm_year  год           (надо прибавлять 1900)
    tm_yday  день в году   0..365
    tm_mon   номер месяца  0..11 (0 - Январь)
    tm_mday  дата месяца   1..31
    tm_wday  день недели   0..6  (0 - Воскресенье)
    tm_hour  часы          0..23
    tm_min   минуты        0..59
    tm_sec   секунды       0..59

Номера месяца и дня недели начинаются с нуля, чтобы вы могли использовать их в качестве индексов:

    char *months[] = { "Январь", "Февраль", ..., "Декабрь" };
    printf( "%s\n", months[ tm->tm_mon ] );

Пример использования этих функций есть в приложении. Установить время в системе может суперпользователь вызовом

     stime(&t);

6.2.2. Напишите функцию печати текущего времени в формате ЧЧ:ММ:СС ДД-МЕС-ГГ. Используйте системный вызов time() и функцию localtime().

Существует стандартная функция ctime(), которая печатает время в формате:

    /* Mon Mar 25 18:56:36 1991 */
    #include <stdio.h>
    #include <time.h>
    main(){ /* команда date */
            time_t t = time(NULL);
            char *s  = ctime(&t);
            printf("%s", s);
    }

Обратите внимание, что строка s уже содержит на конце символ '\n'.

6.2.3. Структура stat, заполняемая системным вызовом stat(), кроме прочих полей содержит поля типа time_t st_ctime, st_mtime и st_atime - время последнего изменения содержимого I-узла файла, время последнего изменения файла и время последнего доступа к файлу.

  • Поле st_ctime изменяется (устанавливается равным текущему астрономическому времени) при применении к файлу вызовов creat, chmod, chown, link, unlink, mknod, utime*, write (т.к. изменяется длина файла); Это поле следует рассматривать как время модификации прав доступа к файлу;
  • st_mtime - write, creat, mknod, utime; Это поле следует рассматривать как время модификации содержимого файла (данных);
  • st_atime - read, creat, mknod, utime; Это поле следует рассматривать как время чтения содержимого файла (данных).

Модифицируйте функцию typeOf(), чтобы она печатала еще и эти даты.

    utime(имяФайла, NULL);

Он используется для взаимодействия с программой make - в команде touch. Изменить время можно только своему файлу.

6.2.4. Напишите аналог команды ls -tm, выдающей список имен файлов текущего каталога, отсортированный по убыванию поля st_mtime, то есть недавно модифицированные файлы выдаются первыми. Для каждого прочитанного из каталога имени надо сделать stat; имена файлов и времена следует сохранить в массиве структур, а затем отсортировать его.

6.2.5. Напишите аналогичную программу, сортирующую файлы в порядке возрастания их размера (st_size).

6.2.6. Напишите аналог команды ls -l, выдающий имена файлов каталога и их коды доступа в формате rwxrw-r--. Для получения кодов доступа используйте вызов stat

    stat( имяФайла, &st);
    кодыДоступа = st.st_mode & 0777;

Для изменения кодов доступа используется вызов

    chmod(имя_файла, новые_коды);

Можно изменять коды доступа, соответствующие битовой маске

    0777 | S_ISUID | S_ISGID | S_ISVTX

(смотри <sys/stat.h>). Тип файла (см. функцию typeOf) не может быть изменен. Изменить коды доступа к файлу может только его владелец.

Печатайте еще номер I-узла файла: поле d_ino каталога либо поле st_ino структуры stat.

6.2.7. Вот программа, которая каждые 2 секунды проверяет - не изменилось ли содержимое текущего каталога:

    #include <sys/types.h>
    #include <sys/stat.h>
    extern char *ctime();
    main(){
       time_t last; struct stat st;
       for( stat(".", &st), last=st.st_mtime; ; sleep(2)){
            stat(".", &st);
            if(last != st.st_mtime){
               last  = st.st_mtime;
    printf("Был создан или удален какой-то файл: %s",
                       ctime(&last));
            }
       }
    }

Модифицируйте ее, чтобы она сообщала какое имя (имена) было удалено или создано (для этого надо при запуске программы прочитать и запомнить содержимое каталога, а при обнаружении модификации - перечитать каталог и сравнить его с прежним содержимым).

6.2.8. Напишите по аналогии программу, которая выдает сообщение, если указанный вами файл был кем-то прочитан, записан или удален. Вам следует отслеживать изменение полей st_atime, st_mtime и значение stat() < 0 соответственно. Если файл удален - программа завершается.

6.2.9. Современные UNIX-машины имеют встроенные таймеры (как правило несколько) с довольно высоким разрешением. Некоторые из них могут использоваться как "будильники" с обратным отсчетом времени: в таймер загружается некоторое значение; таймер ведет обратный отсчет, уменьшая загруженный счетчик; как только это время истекает - посылается сигнал процессу, загрузившему таймер.

Вот как, к примеру, выглядит функция задержки в микросекундах (миллионных долях секунды). Примечание: эту функцию не следует использовать вперемежку с функциями sleep и alarm (смотри статью про них ниже, в главе про сигналы).

    #include <sys/types.h>
    #include <signal.h>
    #include <sys/time.h>
    void do_nothing() {}
    /* Задержка на usec миллионных долей секунды (микросекунд) */
    void usleep(unsigned int usec) {
            struct itimerval        new, old;
            /*  struct itimerval  содержит поля:
                struct timeval    it_interval;
                struct timeval    it_value;
                Где struct timeval содержит поля:
                long    tv_sec;    -- число целых секунд
                long    tv_usec;   -- число микросекунд
             */
            struct sigaction        new_vec, old_vec;
            if (usec == 0) return;
            /* Поле tv_sec  содержит число целых секунд.
               Поле tv_usec содержит число микросекунд.
               it_value    - это время, через которое В ПЕРВЫЙ раз
                             таймер "прозвонит",
                             то есть пошлет нашему процессу
                             сигнал SIGALRM.
                             Время, равное нулю, немедленно остановит таймер.
               it_interval - это интервал времени, который будет загружаться
                             в таймер после каждого "звонка"
                             (но не в первый раз).
                             Время, равное нулю, остановит таймер
                             после его первого "звонка".
             */
            new.it_interval.tv_sec  = 0;
            new.it_interval.tv_usec = 0;
            new.it_value.tv_sec  = usec / 1000000;
            new.it_value.tv_usec = usec % 1000000;
            /* Сохраняем прежнюю реакцию на сигнал SIGALRM в old_vec,
               заносим в качестве новой реакции do_nothing()
             */
            new_vec.sa_handler = do_nothing;
            sigemptyset(&new_vec.sa_mask);
            new_vec.sa_flags = 0;
            sighold(SIGALRM);
            sigaction(SIGALRM, &new_vec, &old_vec);
            /* Загрузка интервального таймера значением new, начало отсчета.
             * Прежнее значение спасти в old.
             * Вместо &old можно также NULL - не спасать.
             */
            setitimer(ITIMER_REAL, &new, &old);
            /* Ждать прихода сигнала SIGALRM */
            sigpause(SIGALRM);
            /* Восстановить реакцию на SIGALRM */
            sigaction(SIGALRM, &old_vec, (struct sigaction *) 0);
            sigrelse(SIGALRM);
            /* Восстановить прежние параметры таймера */
            setitimer(ITIMER_REAL, &old, (struct itimerval *) 0);
    }

* - Время модификации файла можно изменить на текущее астрономическое время и не производя записи в файл. Для этого используется вызов

© Copyright А. Богатырев, 1992-95
Си в UNIX

Назад | Содержание | Вперед